From 5269ce81c0ea4fbfa141cc886fc120e23b21625e Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 27 Nov 2016 13:33:52 +1300 Subject: [PATCH] -Fixed serializing protected override properties --- .../Serialization/JsonSerializerTest.cs | 61 ++++++++++++++----- .../Utilities/ReflectionUtils.cs | 52 ++++++++-------- 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 47f1f809..180d640b 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -552,8 +552,8 @@ namespace Newtonsoft.Json.Tests.Serialization public void IncompleteContainers() { ExceptionAssert.Throws( - () => JsonConvert.DeserializeObject>("[1,"), - "Unexpected end when deserializing array. Path '[0]', line 1, position 3."); + () => JsonConvert.DeserializeObject>("[1,"), + "Unexpected end when deserializing array. Path '[0]', line 1, position 3."); ExceptionAssert.Throws( () => JsonConvert.DeserializeObject>("[1,"), @@ -1817,7 +1817,7 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.IsNotNull(deserialized.foo); Assert.AreEqual("value", deserialized.foo.bar); } - + [Test] public void ConversionOperator() { @@ -3273,7 +3273,10 @@ keyword such as type of business."" testClass.co = new Co(); String strFromTest = JsonConvert.SerializeObject(testClass); - ExceptionAssert.Throws(() => { InterfacePropertyTestClass testFromDe = (InterfacePropertyTestClass)JsonConvert.DeserializeObject(strFromTest, typeof(InterfacePropertyTestClass)); }, @"Could not create an instance of type Newtonsoft.Json.Tests.TestObjects.ICo. Type is an interface or abstract class and cannot be instantiated. Path 'co.Name', line 1, position 14."); + ExceptionAssert.Throws(() => + { + InterfacePropertyTestClass testFromDe = (InterfacePropertyTestClass)JsonConvert.DeserializeObject(strFromTest, typeof(InterfacePropertyTestClass)); + }, @"Could not create an instance of type Newtonsoft.Json.Tests.TestObjects.ICo. Type is an interface or abstract class and cannot be instantiated. Path 'co.Name', line 1, position 14."); } private Person GetPerson() @@ -7200,7 +7203,10 @@ Path '', line 1, position 1."); null ]"; - ExceptionAssert.Throws(() => { List numbers = JsonConvert.DeserializeObject>(json); }, "Error converting value {null} to type 'System.Int32'. Path '[3]', line 5, position 6."); + ExceptionAssert.Throws(() => + { + List numbers = JsonConvert.DeserializeObject>(json); + }, "Error converting value {null} to type 'System.Int32'. Path '[3]', line 5, position 6."); } #if !(PORTABLE) @@ -8229,7 +8235,7 @@ Path '', line 1, position 1."); KeyValuePair result = JsonConvert.DeserializeObject>( "{key: 123, \"VALUE\": \"test value\"}" - ); + ); Assert.AreEqual(123, result.Key); Assert.AreEqual("test value", result.Value); @@ -8950,7 +8956,7 @@ Path '', line 1, position 1."); { string json = JsonConvert.SerializeObject( new Dictionary { { new Size(1, 2), new Size(3, 4) } } - ); + ); Assert.AreEqual(@"{""1, 2"":""3, 4""}", json); @@ -9678,13 +9684,13 @@ Path '', line 1, position 1."); }"; ExceptionAssert.Throws(() => - { - JsonConvert.DeserializeObject( - JsonMessage, - new MailAddressReadConverter(), - new AttachmentReadConverter(), - new EncodingReadConverter()); - }, + { + JsonConvert.DeserializeObject( + JsonMessage, + new MailAddressReadConverter(), + new AttachmentReadConverter(), + new EncodingReadConverter()); + }, "Cannot populate list type System.Net.Mime.HeaderCollection. Path 'Headers', line 26, position 14."); } @@ -9789,6 +9795,33 @@ Path '', line 1, position 1."); ExceptionAssert.Throws(() => JsonConvert.DeserializeObject(s), "Unexpected end when deserializing object. Path 'dataSource', line 1, position 63."); } + + [Test] + public void ChildClassWithProtectedOverridePlusJsonProperty_Serialize() + { + var propertyValue = "test"; + var testJson = @"{ 'MyProperty' : '" + propertyValue + "' }"; + + var testObject = JsonConvert.DeserializeObject(testJson); + + Assert.AreEqual(propertyValue, testObject.GetPropertyValue(), "MyProperty should be populated"); + } + } + + public class BaseClassWithProtectedVirtual + { + protected virtual string MyProperty { get; set; } + } + + public class ChildClassWithProtectedOverridePlusJsonProperty : BaseClassWithProtectedVirtual + { + [JsonProperty] + protected override string MyProperty { get; set; } + + public string GetPropertyValue() + { + return MyProperty; + } } public class DerivedWithPrivate : BaseWithPrivate diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 17d8a224..55a0d4e4 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -98,13 +98,13 @@ namespace Newtonsoft.Json.Utilities { ValidationUtils.ArgumentNotNull(propertyInfo, nameof(propertyInfo)); - MethodInfo m = propertyInfo.GetGetMethod(); + MethodInfo m = propertyInfo.GetGetMethod(true); if (m != null && m.IsVirtual) { return true; } - m = propertyInfo.GetSetMethod(); + m = propertyInfo.GetSetMethod(true); if (m != null && m.IsVirtual) { return true; @@ -1017,31 +1017,31 @@ namespace Newtonsoft.Json.Utilities { PropertyInfo subTypeProperty = propertyInfo; - if (!IsPublic(subTypeProperty)) + if (!subTypeProperty.IsVirtual()) { - // have to test on name rather than reference because instances are different - // depending on the type that GetProperties was called on - int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name); - if (index == -1) + if (!IsPublic(subTypeProperty)) { - initialProperties.Add(subTypeProperty); - } - else - { - PropertyInfo childProperty = initialProperties[index]; - // don't replace public child with private base - if (!IsPublic(childProperty)) + // have to test on name rather than reference because instances are different + // depending on the type that GetProperties was called on + int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name); + if (index == -1) { - // replace nonpublic properties for a child, but gotten from - // the parent with the one from the child - // the property gotten from the child will have access to private getter/setter - initialProperties[index] = subTypeProperty; + initialProperties.Add(subTypeProperty); + } + else + { + PropertyInfo childProperty = initialProperties[index]; + // don't replace public child with private base + if (!IsPublic(childProperty)) + { + // replace nonpublic properties for a child, but gotten from + // the parent with the one from the child + // the property gotten from the child will have access to private getter/setter + initialProperties[index] = subTypeProperty; + } } } - } - else - { - if (!subTypeProperty.IsVirtual()) + else { int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name && p.DeclaringType == subTypeProperty.DeclaringType); @@ -1051,12 +1051,16 @@ namespace Newtonsoft.Json.Utilities initialProperties.Add(subTypeProperty); } } - else + } + else + { + MethodInfo subTypePropertyBaseDefinition = subTypeProperty.GetBaseDefinition(); + if (subTypePropertyBaseDefinition != null) { int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name && p.IsVirtual() && p.GetBaseDefinition() != null - && p.GetBaseDefinition().DeclaringType.IsAssignableFrom(subTypeProperty.GetBaseDefinition().DeclaringType)); + && p.GetBaseDefinition().DeclaringType.IsAssignableFrom(subTypePropertyBaseDefinition.DeclaringType)); if (index == -1) {