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

How to Omit Inherited Property From JSON.NET Serialization

如果我有以下 类 我想使用 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 对象中的最有效方法是什么?

如果 OrganizationThing 的唯一顶级后代,并且 Organization 类型的字段也不会出现在序列化对象中,您可以通过定义 ShouldSerializeContextThing 中如下:

[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 不允许使用 System.Runtime.Serialization 中的 DataContractAttributeDataMemberAttribute 所以我不得不在这个演示中注释掉相关行。

虽然@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();
}