From 6562d9d31cedc5bba570a182ed4c180c02cf6f0f Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 29 Dec 2013 19:45:41 +1300 Subject: [PATCH] -Fixed using extension data with a non-default constructor --- .../Serialization/JsonSerializerTest.cs | 78 +++++++++++++++++++ Src/Newtonsoft.Json.Tests/TestFixtureBase.cs | 2 +- .../Serialization/ExpressionValueProvider.cs | 2 +- .../JsonSerializerInternalReader.cs | 30 +++++-- 4 files changed, 103 insertions(+), 9 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 9c9f92c8..ff3faa01 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -604,6 +604,19 @@ namespace Newtonsoft.Json.Tests.Serialization public IDictionary ExtensionData; } + public class PublicExtensionDataAttributeTestClassWithNonDefaultConstructor + { + public string Name { get; set; } + + public PublicExtensionDataAttributeTestClassWithNonDefaultConstructor(string name) + { + Name = name; + } + + [JsonExtensionData] + public IDictionary ExtensionData; + } + public class PublicNoReadExtensionDataAttributeTestClass { public string Name { get; set; } @@ -785,6 +798,34 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(2m, bizzaroC2.HourlyWage); } + [Test] + public void DeserializePublicExtensionDataTypeNamdHandlingNonDefaultConstructor() + { + string json = @"{ + ""$id"": ""1"", + ""Name"": ""Name!"", + ""Test"": 1, + ""Self"": { + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.WagePerson, Newtonsoft.Json.Tests"", + ""HourlyWage"": 2.0, + ""Name"": null, + ""BirthDate"": ""0001-01-01T00:00:00"", + ""LastModified"": ""0001-01-01T00:00:00"" + } +}"; + + PublicExtensionDataAttributeTestClassWithNonDefaultConstructor c2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + + Assert.AreEqual("Name!", c2.Name); + + WagePerson bizzaroC2 = (WagePerson)c2.ExtensionData["Self"]; + + Assert.AreEqual(2m, bizzaroC2.HourlyWage); + } + [Test] public void SerializePublicExtensionDataTypeNamdHandling() { @@ -8715,6 +8756,43 @@ Parameter name: value", Assert.AreEqual("EventName!", values2[0][0, 0].EventName); } + public class ExtensionDataDeserializeWithNonDefaultConstructor + { + public ExtensionDataDeserializeWithNonDefaultConstructor(string name) + { + Name = name; + } + + [JsonExtensionData] + public IDictionary _extensionData; + + public string Name { get; set; } + } + + [Test] + public void ExtensionDataDeserializeWithNonDefaultConstructorTest() + { + ExtensionDataDeserializeWithNonDefaultConstructor c = new ExtensionDataDeserializeWithNonDefaultConstructor("Name!"); + c._extensionData = new Dictionary + { + {"Key!", "Value!"} + }; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": ""Name!"", + ""Key!"": ""Value!"" +}", json); + + var c2 = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Name!", c2.Name); + Assert.IsNotNull(c2._extensionData); + Assert.AreEqual(1, c2._extensionData.Count); + Assert.AreEqual("Value!", (string)c2._extensionData["Key!"]); + } + #if NETFX_CORE [Test] public void SerializeWinRTJsonObject() diff --git a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs index 3ff2597f..5a16f609 100644 --- a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs +++ b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs @@ -178,7 +178,7 @@ namespace Newtonsoft.Json.Tests { try { - action(); + ; action(); Assert.Fail("Exception of type {0} expected; got none exception", typeof(TException).Name); } diff --git a/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs b/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs index 47102d5e..ae1e7cba 100644 --- a/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs +++ b/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs @@ -75,7 +75,7 @@ namespace Newtonsoft.Json.Serialization if (!ReflectionUtils.IsNullable(ReflectionUtils.GetMemberUnderlyingType(_memberInfo))) throw new JsonSerializationException("Incompatible value. Cannot set {0} to null.".FormatWith(CultureInfo.InvariantCulture, _memberInfo)); } - else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsAssignableFrom(value.GetType())) + else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsInstanceOfType(value)) { throw new JsonSerializationException("Incompatible value. Cannot set {0} to type {1}.".FormatWith(CultureInfo.InvariantCulture, _memberInfo, value.GetType())); } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 3e1c084a..a1eed470 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -1458,7 +1458,8 @@ To fix this error either change the environment to be fully trusted, change the if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Deserializing {0} using a non-default constructor '{1}'.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType, constructorInfo)), null); - IDictionary propertyValues = ResolvePropertyAndConstructorValues(contract, containerProperty, reader, objectType); + IDictionary extensionData; + IDictionary propertyValues = ResolvePropertyAndConstructorValues(contract, containerProperty, reader, objectType, out extensionData); IDictionary constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object)null); IDictionary remainingPropertyValues = new Dictionary(); @@ -1493,7 +1494,7 @@ To fix this error either change the environment to be fully trusted, change the JsonProperty property = remainingPropertyValue.Key; object value = remainingPropertyValue.Value; - if (ShouldSetPropertyValue(remainingPropertyValue.Key, remainingPropertyValue.Value)) + if (ShouldSetPropertyValue(property, value)) { property.ValueProvider.SetValue(createdObject, value); } @@ -1537,6 +1538,14 @@ To fix this error either change the environment to be fully trusted, change the } } + if (extensionData != null) + { + foreach (KeyValuePair e in extensionData) + { + contract.ExtensionDataSetter(createdObject, e.Key, e.Value); + } + } + EndObject(createdObject, reader, contract, reader.Depth, propertiesPresence); OnDeserialized(reader, contract, createdObject); @@ -1556,8 +1565,10 @@ To fix this error either change the environment to be fully trusted, change the return value; } - private IDictionary ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType) + private IDictionary ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType, out IDictionary extensionData) { + extensionData = (contract.ExtensionDataSetter != null) ? new Dictionary() : null; + IDictionary propertyValues = new Dictionary(); bool exit = false; do @@ -1594,10 +1605,7 @@ To fix this error either change the environment to be fully trusted, change the propertyValue = CreateValueInternal(reader, property.PropertyType, property.PropertyContract, property, contract, containerProperty, null); propertyValues[property] = propertyValue; - } - else - { - reader.Skip(); + continue; } } else @@ -1610,7 +1618,15 @@ To fix this error either change the environment to be fully trusted, change the if (Serializer._missingMemberHandling == MissingMemberHandling.Error) throw JsonSerializationException.Create(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name)); + } + if (extensionData != null) + { + object value = CreateValueInternal(reader, null, null, null, contract, containerProperty, null); + extensionData[memberName] = value; + } + else + { reader.Skip(); } break;