如何从JSON.NET序列化中省略继承的属性

时间:2017-06-09 09:10:43

标签: c# .net json json.net schema.org

如果我有以下类,我想使用JSON.NET序列化:

[DataContract]
public class Thing
{
    [DataMember(Name = "@context")]
    public string Context => "http://schema.org"
}

[DataContract]
public class Organization : Thing
{
    [DataMember(Name = "address")]
    public Address Address { get; set; }

    ...
}

[DataContract]
public class Address : Thing
{
    ...
}

当我使用JSON.NET序列化组织时,我得到:

{
  "@context": "http://schema.org",
  "address": {
    "@context": "http://schema.org",
    ...
  }
  ...
}

确保@context属性仅出现在顶级Organization对象而不是Address对象中的最有效方法是什么?

2 个答案:

答案 0 :(得分:2)

如果OrganizationThing的唯一顶级后代,并且序列化对象中也没有Organization类型的字段,则可以通过定义ShouldSerializeContext轻松完成此操作在Thing中如下:

[DataContract]
public class Thing
{
    [DataMember(Name = "@context")]
    public string Context => "http://schema.org";
    public bool ShouldSerializeContext() { return this is Organization; }
}

演示:https://dotnetfiddle.net/GjmfbA

如果Thing的任何后代可能充当根对象,则可能需要实现自定义转换器。在此转换器的WriteJson方法中,您可以过滤要序列化的属性。要从除根对象以外的所有属性中删除Context属性,请检查writer.Path,这将是根对象的空字符串:

[DataContract]
[JsonConverter(typeof(NoContextConverter))]
public class Thing
{
    [DataMember(Name = "@context")]
    public string Context => "http://schema.org";
}

// ...............

public class NoContextConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var props = value.GetType().GetProperties()
            .Where(p => Attribute.IsDefined(p, typeof(DataMemberAttribute)))
            .ToList();
        if (writer.Path != "")
            props.RemoveAll(p => p.Name == "Context");

        writer.WriteStartObject();
        foreach (var prop in props)
        {
            writer.WritePropertyName(prop.GetCustomAttribute<DataMemberAttribute>().Name);
            serializer.Serialize(writer, prop.GetValue(value, null));
        }
        writer.WriteEndObject();
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(Thing).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

演示:https://dotnetfiddle.net/cIlXID

N.B。出于某种原因,dotnetfiddle.net不允许使用DataContractAttribute中的DataMemberAttributeSystem.Runtime.Serialization,因此我必须在此演示中注释掉相关的行。

答案 1 :(得分:0)

虽然@ DimitryEgorov的答案可能是正确的方法,但它使用反射使其变慢。在下面的解决方案中,我使用StringBuilder在最终的JSON上进行字符串替换。

private const string ContextPropertyJson = "\"@context\":\"http://schema.org\",";

public override string ToString() => RemoveAllButFirstContext(
    JsonConvert.SerializeObject(this, new JsonSerializerSettings));

private static string RemoveAllButFirstContext(string json)
{
    var stringBuilder = new StringBuilder(json);
    var startIndex = ContextPropertyJson.Length + 1;
    stringBuilder.Replace(
        ContextPropertyJson, 
        string.Empty, 
        startIndex, 
        stringBuilder.Length - startIndex - 1);
    return stringBuilder.ToString();
}