diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 813250c1..1c4dd3e5 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -41,5 +41,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.13721")] +[assembly: AssemblyFileVersion("4.0.2.13722")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 73c50a00..821e3350 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -4329,5 +4329,117 @@ keyword such as type of business."" string json = JsonConvert.SerializeObject(new EnumerableArrayPropertyClass()); JsonConvert.DeserializeObject(json); } + +#if !NET20 + [DataContract] + public class BaseDataContract + { + [DataMember(Name = "virtualMember")] + public virtual string VirtualMember { get; set; } + + [DataMember(Name = "nonVirtualMember")] + public string NonVirtualMember { get; set; } + } + + public class ChildDataContract : BaseDataContract + { + public override string VirtualMember { get; set; } + public string NewMember { get; set; } + } + + [Test] + public void ChildDataContractTest() + { + ChildDataContract cc = new ChildDataContract + { + VirtualMember = "VirtualMember!", + NonVirtualMember = "NonVirtualMember!" + }; + + string result = JsonConvert.SerializeObject(cc); + Assert.AreEqual(@"{""virtualMember"":""VirtualMember!"",""nonVirtualMember"":""NonVirtualMember!""}", result); + } +#endif + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class BaseObject + { + [JsonProperty(PropertyName = "virtualMember")] + public virtual string VirtualMember { get; set; } + + [JsonProperty(PropertyName = "nonVirtualMember")] + public string NonVirtualMember { get; set; } + } + + public class ChildObject : BaseObject + { + public override string VirtualMember { get; set; } + public string NewMember { get; set; } + } + + public class ChildWithDifferentOverrideObject : BaseObject + { + [JsonProperty(PropertyName = "differentVirtualMember")] + public override string VirtualMember { get; set; } + } + + [Test] + public void ChildObjectTest() + { + ChildObject cc = new ChildObject + { + VirtualMember = "VirtualMember!", + NonVirtualMember = "NonVirtualMember!" + }; + + string result = JsonConvert.SerializeObject(cc); + Assert.AreEqual(@"{""virtualMember"":""VirtualMember!"",""nonVirtualMember"":""NonVirtualMember!""}", result); + } + + [Test] + public void ChildWithDifferentOverrideObjectTest() + { + ChildWithDifferentOverrideObject cc = new ChildWithDifferentOverrideObject + { + VirtualMember = "VirtualMember!", + NonVirtualMember = "NonVirtualMember!" + }; + + string result = JsonConvert.SerializeObject(cc); + Assert.AreEqual(@"{""differentVirtualMember"":""VirtualMember!"",""nonVirtualMember"":""NonVirtualMember!""}", result); + } + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public interface IInterfaceObject + { + [JsonProperty(PropertyName = "virtualMember")] + [JsonConverter(typeof(IsoDateTimeConverter))] + DateTime InterfaceMember { get; set; } + } + + public class ImplementInterfaceObject : IInterfaceObject + { + public DateTime InterfaceMember { get; set; } + public string NewMember { get; set; } + [JsonProperty(PropertyName = "newMemberWithProperty")] + public string NewMemberWithProperty { get; set; } + } + + [Test] + public void ImplementInterfaceObjectTest() + { + ImplementInterfaceObject cc = new ImplementInterfaceObject + { + InterfaceMember = new DateTime(2010, 12, 31, 0, 0, 0, DateTimeKind.Utc), + NewMember = "NewMember!" + }; + + string result = JsonConvert.SerializeObject(cc, Formatting.Indented); + + Assert.AreEqual(@"{ + ""virtualMember"": ""2010-12-31T00:00:00Z"", + ""newMemberWithProperty"": null +}", result); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index b1c274d8..e37cbec1 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -110,7 +110,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.13721")] +[assembly: AssemblyFileVersion("4.0.2.13722")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index cc1cd298..8078c046 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -766,8 +766,8 @@ namespace Newtonsoft.Json.Serialization DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(declaringType); DataMemberAttribute dataMemberAttribute; - if (dataContractAttribute != null) - dataMemberAttribute = JsonTypeReflector.GetAttribute(attributeProvider); + if (dataContractAttribute != null && attributeProvider is MemberInfo) + dataMemberAttribute = JsonTypeReflector.GetDataMemberAttribute((MemberInfo)attributeProvider); else dataMemberAttribute = null; #endif diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index e1f0857d..c4e31571 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -96,7 +96,48 @@ namespace Newtonsoft.Json.Serialization #if !PocketPC && !NET20 public static DataContractAttribute GetDataContractAttribute(Type type) { - return CachedAttributeGetter.GetAttribute(type); + // DataContractAttribute does not have inheritance + DataContractAttribute result = null; + Type currentType = type; + while (result == null && currentType != null) + { + result = CachedAttributeGetter.GetAttribute(currentType); + currentType = currentType.BaseType; + } + + return result; + } + + public static DataMemberAttribute GetDataMemberAttribute(MemberInfo memberInfo) + { + // DataMemberAttribute does not have inheritance + + // can't override a field + if (memberInfo.MemberType == MemberTypes.Field) + return CachedAttributeGetter.GetAttribute(memberInfo); + + // search property and then search base properties if nothing is returned and the property is virtual + PropertyInfo propertyInfo = (PropertyInfo) memberInfo; + DataMemberAttribute result = CachedAttributeGetter.GetAttribute(propertyInfo); + if (result == null) + { + if (propertyInfo.IsVirtual()) + { + Type currentType = propertyInfo.DeclaringType; + Type[] types = propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(); + + while (result == null && currentType != null) + { + PropertyInfo baseProperty = (PropertyInfo)ReflectionUtils.GetMemberInfoFromType(currentType, propertyInfo); + if (baseProperty != null && baseProperty.IsVirtual()) + result = CachedAttributeGetter.GetAttribute(baseProperty); + + currentType = currentType.BaseType; + } + } + } + + return result; } #endif @@ -204,38 +245,72 @@ namespace Newtonsoft.Json.Serialization return _cachedMetadataTypeAttributeType; } +#endif private static T GetAttribute(Type type) where T : Attribute { + T attribute; + +#if !SILVERLIGHT && !PocketPC && !NET20 Type metadataType = GetAssociatedMetadataType(type); if (metadataType != null) { - T attribute = ReflectionUtils.GetAttribute(metadataType, true); + attribute = ReflectionUtils.GetAttribute(metadataType, true); + if (attribute != null) + return attribute; + } +#endif + + attribute = ReflectionUtils.GetAttribute(type, true); + if (attribute != null) + return attribute; + + foreach (Type typeInterface in type.GetInterfaces()) + { + attribute = ReflectionUtils.GetAttribute(typeInterface, true); if (attribute != null) return attribute; } - return ReflectionUtils.GetAttribute(type, true); + return null; } private static T GetAttribute(MemberInfo memberInfo) where T : Attribute { + T attribute; + +#if !SILVERLIGHT && !PocketPC && !NET20 Type metadataType = GetAssociatedMetadataType(memberInfo.DeclaringType); if (metadataType != null) { - MemberInfo metadataTypeMemberInfo = metadataType.GetMember(memberInfo.Name, - memberInfo.MemberType, - BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance).SingleOrDefault(); + MemberInfo metadataTypeMemberInfo = ReflectionUtils.GetMemberInfoFromType(metadataType, memberInfo); if (metadataTypeMemberInfo != null) { - T attribute = ReflectionUtils.GetAttribute(metadataTypeMemberInfo, true); + attribute = ReflectionUtils.GetAttribute(metadataTypeMemberInfo, true); + if (attribute != null) + return attribute; + } + } +#endif + + attribute = ReflectionUtils.GetAttribute(memberInfo, true); + if (attribute != null) + return attribute; + + foreach (Type typeInterface in memberInfo.DeclaringType.GetInterfaces()) + { + MemberInfo interfaceTypeMemberInfo = ReflectionUtils.GetMemberInfoFromType(typeInterface, memberInfo); + + if (interfaceTypeMemberInfo != null) + { + attribute = ReflectionUtils.GetAttribute(interfaceTypeMemberInfo, true); if (attribute != null) return attribute; } } - return ReflectionUtils.GetAttribute(memberInfo, true); + return null; } public static T GetAttribute(ICustomAttributeProvider attributeProvider) where T : Attribute @@ -250,12 +325,6 @@ namespace Newtonsoft.Json.Serialization return ReflectionUtils.GetAttribute(attributeProvider, true); } -#else - public static T GetAttribute(ICustomAttributeProvider attributeProvider) where T : Attribute - { - return ReflectionUtils.GetAttribute(attributeProvider, true); - } -#endif private static bool? _dynamicCodeGeneration; diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 69a1571a..a125e043 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -37,6 +37,21 @@ namespace Newtonsoft.Json.Utilities { internal static class ReflectionUtils { + public static bool IsVirtual(this PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + MethodInfo m = propertyInfo.GetGetMethod(); + if (m != null && m.IsVirtual) + return true; + + m = propertyInfo.GetSetMethod(); + if (m != null && m.IsVirtual) + return true; + + return false; + } + public static Type GetObjectType(object v) { return (v != null) ? v.GetType() : null; @@ -706,6 +721,9 @@ namespace Newtonsoft.Json.Utilities // http://hyperthink.net/blog/getcustomattributes-gotcha/ // ICustomAttributeProvider doesn't do inheritance + if (attributeProvider is Type) + return (T[])((Type)attributeProvider).GetCustomAttributes(typeof(T), inherit); + if (attributeProvider is Assembly) return (T[])Attribute.GetCustomAttributes((Assembly)attributeProvider, typeof(T), inherit); @@ -851,6 +869,23 @@ namespace Newtonsoft.Json.Utilities return null; } + public static MemberInfo GetMemberInfoFromType(Type targetType, MemberInfo memberInfo) + { + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + + switch (memberInfo.MemberType) + { + case MemberTypes.Property: + PropertyInfo propertyInfo = (PropertyInfo) memberInfo; + + Type[] types = propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(); + + return targetType.GetProperty(propertyInfo.Name, bindingAttr, null, propertyInfo.PropertyType, types, null); + default: + return targetType.GetMember(memberInfo.Name, memberInfo.MemberType, bindingAttr).SingleOrDefault(); + } + } + public static IEnumerable GetFields(Type targetType, BindingFlags bindingAttr) { ValidationUtils.ArgumentNotNull(targetType, "targetType"); @@ -896,9 +931,7 @@ namespace Newtonsoft.Json.Utilities PropertyInfo member = propertyInfos[i]; if (member.DeclaringType != targetType) { - Type[] types = member.GetIndexParameters().Select(p => p.ParameterType).ToArray(); - - PropertyInfo declaredMember = member.DeclaringType.GetProperty(member.Name, bindingAttr, null, member.PropertyType, types, null); + PropertyInfo declaredMember = (PropertyInfo)GetMemberInfoFromType(member.DeclaringType, member); propertyInfos[i] = declaredMember; } }