【问题标题】:JsonPropertyAttribute ignored on private property in derived class派生类中私有属性上的 JsonPropertyAttribute 被忽略
【发布时间】:2015-01-11 13:47:03
【问题描述】:

在序列化具有私有属性的派生对象时,我遇到了 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”。我发现这项工作的唯一方法是该属性是受保护的或公共的,并在子类中被覆盖。

为什么会这样?这是一个错误吗?

【问题讨论】:

  • 您是否尝试过在您的财产上使用new 关键字?
  • 我有。仅覆盖作品
  • 使用私有字段而不是私有属性。它将被正确序列化。

标签: c# .net json json.net


【解决方案1】:

看起来这是 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"
}

【讨论】:

  • 谢谢。我会在 GitHub 上询问并报告。
  • 哦,我完全忘记了这一点。这是GitHub issue。它可能并不完全令人满意,但我想我们必须忍受它:)
猜你喜欢
  • 2011-07-13
  • 2011-10-03
  • 1970-01-01
  • 1970-01-01
  • 2013-12-26
  • 2013-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多