在Json.NET序列化中忽略基类属性

时间:2015-06-28 20:17:36

标签: c# json serialization json.net

我有以下类结构:

[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,并显示消息&#34;检测到属性&#39;信封&#39;使用类型&#39; MyNamespace.AxisAlignedRectangle&#39;。&#34;如果我将[JsonObject(IsReference = true)]as described here)添加到AxisAlignedRectangle,代码运行正常,但我在AxisAlignedRectangle的每个实例中获得一个自动分配的$ id字段,并在该实例重新获得时返回$ ref字段引用。例如,当我序列化多边形时,我得到:

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

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

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

3 个答案:

答案 0 :(得分:13)

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

在一个句子中,它将仅序列化用[JsonProperty]属性修饰的属性。 您可以在此处阅读更多内容:MemberSerialization Enumeration

另一种选择是用JsonIgnoreAttribute Class装饰Polygon属性。

答案 1 :(得分:5)

您可以使用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

答案 2 :(得分:3)

我遇到了同样的事情。 JsonIgnoreAttribute是一个很好的解决方案,如果应该始终使用某个属性并且您可以访问包含该属性的类。但是,如果要确定在序列化时应序列化哪些属性,则可以使用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()代码。