在序列化具有私有属性的派生对象时,我遇到了Json.Net的问题。好像
public class Base
{
[JsonProperty]
private string Type { get { return "Base"; } }
}
public class Inherited : Base
{
[JsonProperty]
private string Type { get { return "Inherited"; } }
}
当我序列化Inherited
的实例时,Type
属性始终设置为“Base”。我发现工作的唯一方法是属性受保护或公共,并在子类中重写。
为什么这样工作?这是一个错误吗?
答案 0 :(得分:2)
看起来这是Json.NET的预期行为。来自ReflectionUtils.cs:
private static void GetChildPrivateProperties(IList<PropertyInfo> initialProperties, Type targetType, BindingFlags bindingAttr)
{
// fix weirdness with private PropertyInfos only being returned for the current Type
// find base type properties and add them to result
// also find base properties that have been hidden by subtype properties with the same name
while ((targetType = targetType.BaseType()) != null)
{
foreach (PropertyInfo propertyInfo in targetType.GetProperties(bindingAttr))
{
PropertyInfo subTypeProperty = propertyInfo;
if (!IsPublic(subTypeProperty))
{
// 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)
{
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;
}
这是生成类型的属性列表的位置,正如您所看到的,有些代码有意将基类中具有相同名称的属性更喜欢继承的类。
我不知道为什么Json.NET会这样做,你可能想报告问题并问为什么。在此期间,您可以使用IContractResolver
来有选择地阻止此行为:
[System.AttributeUsage(AttributeTargets.Property)]
public class JsonPreferDerivedPropertyAttribute : System.Attribute
{
}
public class PreferDerivedPropertyContractResolver : DefaultContractResolver
{
static PropertyInfo GetDerivedPropertyRecursive(Type objectType, Type stopType, PropertyInfo property)
{
var parameters = property.GetIndexParameters().Select(info => info.ParameterType).ToArray();
for (; objectType != null && objectType != stopType; objectType = objectType.BaseType)
{
var derivedProperty = objectType.GetProperty(
property.Name,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, property.PropertyType,
parameters,
null);
if (derivedProperty == null)
continue;
if (derivedProperty == property)
return derivedProperty; // No override.
if (derivedProperty.GetCustomAttribute<JsonPreferDerivedPropertyAttribute>() != null)
return derivedProperty;
}
return null;
}
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var list = base.GetSerializableMembers(objectType);
for (int i = 0; i < list.Count; i++)
{
var property = list[i] as PropertyInfo;
if (property == null)
continue;
if (property.DeclaringType != objectType)
{
var derivedProperty = GetDerivedPropertyRecursive(objectType, property.DeclaringType, property);
if (derivedProperty == null || derivedProperty == property)
continue;
if (derivedProperty != property
&& (property.GetGetMethod(true) == null || derivedProperty.GetGetMethod(true) != null)
&& (property.GetSetMethod(true) == null || derivedProperty.GetSetMethod(true) != null))
{
list[i] = derivedProperty;
}
}
}
return list;
}
}
我建议有选择地这样做,因为我不完全理解为什么Json.NET会做它的功能。上面的代码仅覆盖应用了自定义JsonPreferDerivedPropertyAttribute
属性的派生类属性的默认行为。
然后使用它:
public class Base
{
[JsonProperty]
private string Type { get { return "Base"; } }
}
public class Inherited : Base
{
[JsonProperty]
[JsonPreferDerivedPropertyAttribute]
private string Type { get { return "Inherited"; } }
}
public class VeryInherited : Inherited
{
[JsonProperty]
public string VeryInheritedProperty { get { return "VeryInherited"; } }
}
public static class TestOverride
{
public static void Test()
{
var inherited = new Inherited();
var json1 = JsonConvert.SerializeObject(inherited, Formatting.Indented, new JsonSerializerSettings() { ContractResolver = new PreferDerivedPropertyContractResolver() });
var veryInherited = new VeryInherited();
var json2 = JsonConvert.SerializeObject(veryInherited, Formatting.Indented, new JsonSerializerSettings() { ContractResolver = new PreferDerivedPropertyContractResolver() });
Debug.WriteLine(json1);
Debug.WriteLine(json2);
}
}
输出结果为:
{
"Type": "Inherited"
}
和
{
"VeryInheritedProperty": "VeryInherited",
"Type": "Inherited"
}