如何省略从 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
对象中的最有效方法是什么?
如果 Organization
是 Thing
的唯一顶级后代,并且 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 不允许使用 System.Runtime.Serialization
中的 DataContractAttribute
和 DataMemberAttribute
所以我不得不在这个演示中注释掉相关行。
虽然@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();
}
如果我有以下 类 我想使用 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
对象中的最有效方法是什么?
如果 Organization
是 Thing
的唯一顶级后代,并且 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 不允许使用 System.Runtime.Serialization
中的 DataContractAttribute
和 DataMemberAttribute
所以我不得不在这个演示中注释掉相关行。
虽然@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();
}