【问题标题】:Ignore Base Class Properties in Json.NET Serialization忽略 Json.NET 序列化中的基类属性
【发布时间】:2015-06-28 20:17:35
【问题描述】:

我有以下类结构:

[JsonObject]
public class Polygon : IEnumerable<Point>
{
    public List<Point> Vertices { get; set; }
    public AxisAlignedRectangle Envelope { get; set; }
}

public class AxisAlignedRectangle : Polygon {
    public double Left { get; set; }
    ...
}

我正在序列化 Polygon 类,但是当我这样做时,我得到一个带有消息的 JsonSerializationException

检测到类型为“MyNamespace.AxisAlignedRectangle”的属性“Envelope”的自引用循环。

如果我将[JsonObject(IsReference = true)] (as described here) 添加到 AxisAlignedRectangle,代码运行良好,但我在 AxisAlignedRectangle 的每个实例中获得了一个自动分配的 $id 字段,并在该实例被重新引用时获得了一个 $ref 字段.例如,当我序列化一个多边形时,我得到:

{
    Vertices: [ ... ],
    Envelope: {
        $id: '1',
        Left: -5,
        ...
        Vertices: [ ... ],
        Envelope: {
            $ref: '1'
        }
    }
}

我希望在序列化 AxisAlignedRectangle 时完全删除 Polygon 属性。我尝试将 DataContractAttribute 添加到 AxisAlignedRectangle 类(以及适当的 DataMemberAttribute 属性),但 Polygon 的所有属性仍在序列化中。这是出乎意料的,因为Json.NET documentation 中有一个示例似乎表明这种方法应该有效。

当被序列化的类型是 AxisAlignedRectangle 时,有没有人知道从生成的 Json.NET 序列化中显式删除(最重要的是)Envelope 属性的方法?谢谢。

【问题讨论】:

    标签: c# json serialization json.net


    【解决方案1】:

    最简单的方法就是用[JsonObject(MemberSerialization.OptIn)]装饰AxisAlignedRectangle对象。

    一句话,它只会序列化用 [JsonProperty] 属性修饰的属性。 你可以在这里阅读更多内容:MemberSerialization Enumeration

    另一种选择是使用JsonIgnoreAttribute Class 装饰多边形属性。

    【讨论】:

    • 这个解决方案的问题是我只想在 Polygon 的 N 个子类中的 1 个中禁用 Polygon.Envelope 的序列化。为了让您的建议生效,我必须将 Polygon.Envelope 标记为虚拟,然后在 AxisAlignedRectangle 中覆盖它,以添加 JsonIgnoreAttribute。即使这样可行,我认为标记为答案的解决方案更清晰,因为代码清楚地表明只能更改其成员的序列化,而不是它们的基本行为。
    • 拯救了我的一天,谢谢
    【解决方案2】:

    我遇到了同样的事情。 JsonIgnoreAttribute 是一个很好的解决方案,如果某个属性应该始终是 ingored 并且您可以访问包含该属性的类。但是如果你想确定哪些属性应该在序列化时被序列化,你可以使用 ContractResolver。

    这是一个实现,它允许您从最派生的类开始序列化属性并在给定的基类处停止。就我而言,我想序列化自定义 CMS (EPiServer) 页面类型的属性,但不想序列化页面类的所有内置属性。

    public class DerivedClassContractResolver : DefaultContractResolver
    {
        private Type _stopAtBaseType;
    
        public DerivedClassContractResolver(Type stopAtBaseType) 
        {
            _stopAtBaseType = stopAtBaseType;
        }
    
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            Type originalType = GetOriginalType(type);
            IList<JsonProperty> defaultProperties = base.CreateProperties(type, memberSerialization);
            List<string> includedProperties = Utilities.GetPropertyNames(originalType, _stopAtBaseType);
    
            return defaultProperties.Where(p => includedProperties.Contains(p.PropertyName)).ToList();
        }
    
        private Type GetOriginalType(Type type)
        {
            Type originalType = type;
    
            //If the type is a dynamic proxy, get the base type
            if (typeof(Castle.DynamicProxy.IProxyTargetAccessor).IsAssignableFrom(type))
                originalType = type.BaseType ?? type;
    
            return originalType;
        }
    }
    
    public class Utilities
    {
        /// <summary>
        /// Gets a list of all public instance properties of a given class type
        /// excluding those belonging to or inherited by the given base type.
        /// </summary>
        /// <param name="type">The Type to get property names for</param>
        /// <param name="stopAtType">A base type inherited by type whose properties should not be included.</param>
        /// <returns></returns>
        public static List<string> GetPropertyNames(Type type, Type stopAtBaseType)
        {
            List<string> propertyNames = new List<string>();
    
            if (type == null || type == stopAtBaseType) return propertyNames; 
    
            Type currentType = type;
    
            do
            {
                PropertyInfo[] properties = currentType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
    
                foreach (PropertyInfo property in properties)
                    if (!propertyNames.Contains(property.Name))
                        propertyNames.Add(property.Name);
    
                currentType = currentType.BaseType;
            } while (currentType != null && currentType != stopAtBaseType);
    
            return propertyNames;
        }
    }
    

    这让我做这样的事情:

    JsonConvert.SerializeObject(page, new JsonSerializerSettings() 
        { 
             ContractResolver = new DerivedClassContractResolver(typeof(EPiServer.Core.PageData)) 
        }));
    

    获取我在自己的类中定义的属性,而不需要从 EPiServer.Core.PageData 继承的大量属性。注意:如果您不使用 Castle DynamicProxy project(EPiServer CMS 会这样做),则不需要 GetOriginalType() 代码。

    【讨论】:

    • Brillant, ContractResolver 正是我需要的,也是我认为缺少 JsonSerializer 的。太方便了!
    • 很好的解决方案!感谢分享:)
    【解决方案3】:

    你可以使用conditional property serialization,通过这样定义你的类:

    [JsonObject]
    public class Polygon : IEnumerable<Point>
    {
        public List<Point> Vertices { get; set; }
        public AxisAlignedRectangle Envelope { get; set; }
    
        public virtual bool ShouldSerializeEnvelope()
        {
            return true;
        }
    }
    
    public class AxisAlignedRectangle : Polygon
    {
        public double Left { get; set; }
        ...
    
        public override bool ShouldSerializeEnvelope()
        {
            return false;
        }
    }
    

    我已在以下位置发布了完整的解决方案: https://github.com/thiagoavelino/VisualStudio_C/blob/master/VisualStudio_C/StackOverFlow/ParsingJason/EnvelopePolygonProblem.cs

    【讨论】:

    • 从第三方类继承时不起作用。
    猜你喜欢
    • 2016-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多