From de5f7dd8a810e34ade0ebecedc0b5a67741a236c Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 17 Mar 2014 01:18:39 +1300 Subject: [PATCH] -Fixed hiding generic properties sometimes not being detected --- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 104 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 2 +- .../Utilities/ReflectionUtils.cs | 25 +++-- 4 files changed, 123 insertions(+), 10 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 358c23ba..3ee5554b 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.1.17115")] +[assembly: AssemblyFileVersion("6.0.1.17117")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index f7c4a7de..f6d0aabc 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -306,6 +306,110 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(true, childClass.IsTransient); } + public class ResponseWithNewGenericProperty : SimpleResponse + { + public new T Data { get; set; } + } + + public class ResponseWithNewGenericPropertyVirtual : SimpleResponse + { + public virtual new T Data { get; set; } + } + + public class ResponseWithNewGenericPropertyOverride : ResponseWithNewGenericPropertyVirtual + { + public override T Data { get; set; } + } + + public abstract class SimpleResponse + { + public string Result { get; set; } + public string Message { get; set; } + public object Data { get; set; } + + protected SimpleResponse() + { + + } + + protected SimpleResponse(string message) + { + Message = message; + } + } + + [Test] + public void CanSerializeWithBuiltInTypeAsGenericArgument() + { + var input = new ResponseWithNewGenericProperty() + { + Message = "Trying out integer as type parameter", + Data = 25, + Result = "This should be fine" + }; + + var json = JsonConvert.SerializeObject(input); + var deserialized = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(input.Data, deserialized.Data); + Assert.AreEqual(input.Message, deserialized.Message); + Assert.AreEqual(input.Result, deserialized.Result); + } + + [Test] + public void CanSerializeWithBuiltInTypeAsGenericArgumentVirtual() + { + var input = new ResponseWithNewGenericPropertyVirtual() + { + Message = "Trying out integer as type parameter", + Data = 25, + Result = "This should be fine" + }; + + var json = JsonConvert.SerializeObject(input); + var deserialized = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(input.Data, deserialized.Data); + Assert.AreEqual(input.Message, deserialized.Message); + Assert.AreEqual(input.Result, deserialized.Result); + } + + [Test] + public void CanSerializeWithBuiltInTypeAsGenericArgumentOverride() + { + var input = new ResponseWithNewGenericPropertyOverride() + { + Message = "Trying out integer as type parameter", + Data = 25, + Result = "This should be fine" + }; + + var json = JsonConvert.SerializeObject(input); + var deserialized = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(input.Data, deserialized.Data); + Assert.AreEqual(input.Message, deserialized.Message); + Assert.AreEqual(input.Result, deserialized.Result); + } + + [Test] + public void CanSerializedWithGenericClosedTypeAsArgument() + { + var input = new ResponseWithNewGenericProperty>() + { + Message = "More complex case - generic list of int", + Data = Enumerable.Range(50, 70).ToList(), + Result = "This should be fine too" + }; + + var json = JsonConvert.SerializeObject(input); + var deserialized = JsonConvert.DeserializeObject>>(json); + + Assert.AreEqual(input.Data, deserialized.Data); + Assert.AreEqual(input.Message, deserialized.Message); + Assert.AreEqual(input.Result, deserialized.Result); + } + [Test] public void JsonSerializerProperties() { diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 28da5a13..39cb3234 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.0.1.17115")] +[assembly: AssemblyFileVersion("6.0.1.17117")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 28b9dddd..c98f4cc8 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -597,7 +597,7 @@ namespace Newtonsoft.Json.Utilities targetMembers.AddRange(GetProperties(type, bindingAttr)); // for some reason .NET returns multiple members when overriding a generic member on a base class - // http://forums.msdn.microsoft.com/en-US/netfxbcl/thread/b5abbfee-e292-4a64-8907-4e3f0fb90cd9/ + // http://social.msdn.microsoft.com/Forums/en-US/b5abbfee-e292-4a64-8907-4e3f0fb90cd9/reflection-overriden-abstract-generic-properties?forum=netfxbcl // filter members to only return the override on the topmost class // update: I think this is fixed in .NET 3.5 SP1 - leave this in for now... List distinctMembers = new List(targetMembers.Count); @@ -613,7 +613,14 @@ namespace Newtonsoft.Json.Utilities } else { - var resolvedMembers = members.Where(m => !IsOverridenGenericMember(m, bindingAttr) || m.Name == "Item"); + IList resolvedMembers = new List(); + foreach (MemberInfo memberInfo in members) + { + if (resolvedMembers.Count == 0) + resolvedMembers.Add(memberInfo); + else if (!IsOverridenGenericMember(memberInfo, bindingAttr) || memberInfo.Name == "Item") + resolvedMembers.Add(memberInfo); + } distinctMembers.AddRange(resolvedMembers); } @@ -622,20 +629,22 @@ namespace Newtonsoft.Json.Utilities return distinctMembers; } - private static bool IsOverridenGenericMember(MemberInfo memberInfo, BindingFlags bindingAttr) { - MemberTypes memberType = memberInfo.MemberType(); - if (memberType != MemberTypes.Field && memberType != MemberTypes.Property) - throw new ArgumentException("Member must be a field or property."); + if (memberInfo.MemberType() != MemberTypes.Property) + return false; - Type declaringType = memberInfo.DeclaringType; + PropertyInfo propertyInfo = (PropertyInfo)memberInfo; + if (!IsVirtual(propertyInfo)) + return false; + + Type declaringType = propertyInfo.DeclaringType; if (!declaringType.IsGenericType()) return false; Type genericTypeDefinition = declaringType.GetGenericTypeDefinition(); if (genericTypeDefinition == null) return false; - MemberInfo[] members = genericTypeDefinition.GetMember(memberInfo.Name, bindingAttr); + MemberInfo[] members = genericTypeDefinition.GetMember(propertyInfo.Name, bindingAttr); if (members.Length == 0) return false; Type memberUnderlyingType = GetMemberUnderlyingType(members[0]);