diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index c5c582dd..93fa0877 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -1740,6 +1740,34 @@ namespace Newtonsoft.Json.Tests.Serialization } } } + +#if !NET20 + [Test] + public void ExistingBaseValue() + { + string json = @"{ + ""itemIdentifier"": { + ""$type"": ""Newtonsoft.Json.Tests.Serialization.ReportItemKeys, Newtonsoft.Json.Tests"", + ""dataType"": 0, + ""wantedUnitID"": 1, + ""application"": 3, + ""id"": 101, + ""name"": ""Machine"" + }, + ""isBusinessEntity"": false, + ""isKeyItem"": true, + ""summarizeOnThisItem"": false +}"; + + GroupingInfo g = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + + ReportItemKeys item = (ReportItemKeys)g.ItemIdentifier; + Assert.AreEqual(1, item.WantedUnitID); + } +#endif } #if !NETFX_CORE @@ -1941,6 +1969,42 @@ namespace Newtonsoft.Json.Tests.Serialization public string String { get; set; } public int Integer { get; set; } } + +#if !NET20 + [DataContract] + public class GroupingInfo + { + [DataMember] + public ApplicationItemKeys ItemIdentifier { get; set; } + + public GroupingInfo() + { + ItemIdentifier = new ApplicationItemKeys(); + } + } + + [DataContract] + public class ApplicationItemKeys + { + [DataMember] + public int ID { get; set; } + [DataMember] + public string Name { get; set; } + } + + [DataContract] + public class ReportItemKeys : ApplicationItemKeys + { + protected ulong _wantedUnit; + + [DataMember] + public ulong WantedUnitID + { + get { return _wantedUnit; } + set { _wantedUnit = value; } + } + } +#endif } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index e91e4a29..9b84991f 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -374,6 +374,7 @@ namespace Newtonsoft.Json.Serialization { string id; object newValue; + Type resolvedObjectType = objectType; if (Serializer.SpecialPropertyHandling == SpecialPropertyHandling.ReadAhead) { @@ -389,13 +390,13 @@ namespace Newtonsoft.Json.Serialization reader = tokenReader; } - if (ReadSpecialPropertiesToken(tokenReader, ref objectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) + if (ReadSpecialPropertiesToken(tokenReader, ref resolvedObjectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) return newValue; } else { CheckedRead(reader); - if (ReadSpecialProperties(reader, ref objectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) + if (ReadSpecialProperties(reader, ref resolvedObjectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) return newValue; } @@ -409,7 +410,8 @@ namespace Newtonsoft.Json.Serialization bool createdFromNonDefaultConstructor = false; JsonObjectContract objectContract = (JsonObjectContract)contract; object targetObject; - if (existingValue != null) + // check that if type name handling is being used that the existing value is compatible with the specified type + if (existingValue != null && (resolvedObjectType == objectType || resolvedObjectType.IsInstanceOfType(existingValue))) targetObject = existingValue; else targetObject = CreateNewObject(reader, objectContract, member, containerMember, id, out createdFromNonDefaultConstructor); @@ -433,7 +435,7 @@ namespace Newtonsoft.Json.Serialization if (reader.TokenType == JsonToken.StartObject) throw JsonSerializationException.Create(reader, "Unexpected token when deserializing primitive value: " + reader.TokenType); - object value = CreateValueInternal(reader, objectType, primitiveContract, member, null, null, existingValue); + object value = CreateValueInternal(reader, resolvedObjectType, primitiveContract, member, null, null, existingValue); CheckedRead(reader); return value; @@ -503,7 +505,7 @@ namespace Newtonsoft.Json.Serialization throw JsonSerializationException.Create(reader, @"Cannot deserialize the current JSON object (e.g. {{""name"":""value""}}) into type '{0}' because the type requires a {1} to deserialize correctly. To fix this error either change the JSON to a {1} or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. -".FormatWith(CultureInfo.InvariantCulture, objectType, GetExpectedDescription(contract))); +".FormatWith(CultureInfo.InvariantCulture, resolvedObjectType, GetExpectedDescription(contract))); } private bool ReadSpecialPropertiesToken(JTokenReader reader, ref Type objectType, ref JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue, out object newValue, out string id)