-Fixed serializing objects with DataContract and DataMember attributes on base classes

-JsonObject and JsonProperty attributes can now be placed on an interface and used when serializing implementing objects
This commit is contained in:
JamesNK
2011-05-21 12:55:42 +00:00
parent 76706c6cd2
commit cc44f53c1a
6 changed files with 235 additions and 21 deletions
@@ -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
@@ -4329,5 +4329,117 @@ keyword such as type of business.""
string json = JsonConvert.SerializeObject(new EnumerableArrayPropertyClass());
JsonConvert.DeserializeObject<EnumerableArrayPropertyClass>(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);
}
}
}
@@ -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)]
@@ -766,8 +766,8 @@ namespace Newtonsoft.Json.Serialization
DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(declaringType);
DataMemberAttribute dataMemberAttribute;
if (dataContractAttribute != null)
dataMemberAttribute = JsonTypeReflector.GetAttribute<DataMemberAttribute>(attributeProvider);
if (dataContractAttribute != null && attributeProvider is MemberInfo)
dataMemberAttribute = JsonTypeReflector.GetDataMemberAttribute((MemberInfo)attributeProvider);
else
dataMemberAttribute = null;
#endif
@@ -96,7 +96,48 @@ namespace Newtonsoft.Json.Serialization
#if !PocketPC && !NET20
public static DataContractAttribute GetDataContractAttribute(Type type)
{
return CachedAttributeGetter<DataContractAttribute>.GetAttribute(type);
// DataContractAttribute does not have inheritance
DataContractAttribute result = null;
Type currentType = type;
while (result == null && currentType != null)
{
result = CachedAttributeGetter<DataContractAttribute>.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<DataMemberAttribute>.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<DataMemberAttribute>.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<DataMemberAttribute>.GetAttribute(baseProperty);
currentType = currentType.BaseType;
}
}
}
return result;
}
#endif
@@ -204,38 +245,72 @@ namespace Newtonsoft.Json.Serialization
return _cachedMetadataTypeAttributeType;
}
#endif
private static T GetAttribute<T>(Type type) where T : Attribute
{
T attribute;
#if !SILVERLIGHT && !PocketPC && !NET20
Type metadataType = GetAssociatedMetadataType(type);
if (metadataType != null)
{
T attribute = ReflectionUtils.GetAttribute<T>(metadataType, true);
attribute = ReflectionUtils.GetAttribute<T>(metadataType, true);
if (attribute != null)
return attribute;
}
#endif
attribute = ReflectionUtils.GetAttribute<T>(type, true);
if (attribute != null)
return attribute;
foreach (Type typeInterface in type.GetInterfaces())
{
attribute = ReflectionUtils.GetAttribute<T>(typeInterface, true);
if (attribute != null)
return attribute;
}
return ReflectionUtils.GetAttribute<T>(type, true);
return null;
}
private static T GetAttribute<T>(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<T>(metadataTypeMemberInfo, true);
attribute = ReflectionUtils.GetAttribute<T>(metadataTypeMemberInfo, true);
if (attribute != null)
return attribute;
}
}
#endif
attribute = ReflectionUtils.GetAttribute<T>(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<T>(interfaceTypeMemberInfo, true);
if (attribute != null)
return attribute;
}
}
return ReflectionUtils.GetAttribute<T>(memberInfo, true);
return null;
}
public static T GetAttribute<T>(ICustomAttributeProvider attributeProvider) where T : Attribute
@@ -250,12 +325,6 @@ namespace Newtonsoft.Json.Serialization
return ReflectionUtils.GetAttribute<T>(attributeProvider, true);
}
#else
public static T GetAttribute<T>(ICustomAttributeProvider attributeProvider) where T : Attribute
{
return ReflectionUtils.GetAttribute<T>(attributeProvider, true);
}
#endif
private static bool? _dynamicCodeGeneration;
@@ -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<FieldInfo> 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;
}
}