System.Text.Json/Newtonsoft实现部分子类自定义类型鉴别器需求
问题描述
现有类型结构如下:
public class Person { public string Name { get; set; } } public class Adult : Person { public string Occupation { get; set; } } ... // 更多子类
需要实现的序列化结果(注意第二个条目无__type字段):
[{"__type":"P","Name":"AbcPerson"},{"Occupation":"","Name":"XyzPerson"}]
此前通过System.Web.Script.Serialization.JavaScriptTypeResolver可轻松实现,现需改用System.Text.Json。目前使用了以下特性:
[JsonDerivedType(typeof(Person), typeDiscriminator: "P")] [JsonDerivedType(typeof(Adult))]
但子类数量多且持续新增,仅需为部分子类设置特定鉴别器,其余子类能否使用默认规则?同时也接受Newtonsoft.Json的实现方案,但需将默认的$type字段改为__type。
一、System.Text.Json 实现方案
核心思路
通过自定义JsonConverter<Person>实现精细化控制:仅为标记了自定义特性的类型输出指定的__type鉴别器,其他子类直接序列化、不输出鉴别器字段。新增子类时,只要不需要自定义鉴别器就无需修改配置。
代码实现
- 定义自定义鉴别器特性
[AttributeUsage(AttributeTargets.Class, Inherited = false)] public class CustomTypeDiscriminatorAttribute : Attribute { public string Discriminator { get; } public CustomTypeDiscriminatorAttribute(string discriminator) { Discriminator = discriminator; } } // 给需要自定义鉴别器的类型标记特性 [CustomTypeDiscriminator("P")] public class Person { public string Name { get; set; } } public class Adult : Person { public string Occupation { get; set; } } // 其他子类无需额外标记
- 实现自定义Json转换器
public class PersonConverter : JsonConverter<Person> { // 反序列化逻辑:根据__type字段匹配对应类型 public override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var doc = JsonDocument.ParseValue(ref reader); var root = doc.RootElement; if (root.TryGetProperty("__type", out var discriminatorElem)) { var targetType = discriminatorElem.GetString() switch { "P" => typeof(Person), // 可扩展其他自定义鉴别器的类型映射 _ => typeof(Adult) // 默认匹配子类 }; return (Person)JsonSerializer.Deserialize(root.GetRawText(), targetType, options); } // 无__type字段时默认反序列化为Adult return JsonSerializer.Deserialize<Adult>(root.GetRawText(), options); } // 序列化逻辑:仅标记了自定义特性的类型输出__type字段 public override void Write(Utf8JsonWriter writer, Person value, JsonSerializerOptions options) { var attr = value.GetType().GetCustomAttribute<CustomTypeDiscriminatorAttribute>(); if (attr != null) { writer.WriteStartObject(); writer.WriteString("__type", attr.Discriminator); // 序列化其他属性,避免重复写入__type var element = JsonSerializer.SerializeToElement(value, value.GetType(), options); foreach (var prop in element.EnumerateObject()) { if (prop.Name != "__type") prop.WriteTo(writer); } writer.WriteEndObject(); } else { // 无自定义鉴别器的类型直接序列化,不输出__type JsonSerializer.Serialize(writer, value, value.GetType(), options); } } }
- 使用转换器完成序列化
var options = new JsonSerializerOptions { Converters = { new PersonConverter() }, // 可添加其他配置如忽略空值等 }; var people = new List<Person> { new Person { Name = "AbcPerson" }, new Adult { Name = "XyzPerson", Occupation = "" } }; var json = JsonSerializer.Serialize(people, options); // 输出结果:[{"__type":"P","Name":"AbcPerson"},{"Occupation":"","Name":"XyzPerson"}]
二、Newtonsoft.Json 实现方案
核心思路
通过自定义ISerializationBinder将默认的$type字段替换为__type,再配合自定义转换器控制哪些类型输出鉴别器字段,实现需求效果。
代码实现
- 自定义序列化绑定器(修改鉴别器字段名)
public class CustomSerializationBinder : ISerializationBinder { // 序列化时映射类型到鉴别器名称 public void BindToName(Type serializedType, out string assemblyName, out string typeName) { if (serializedType == typeof(Person)) { typeName = "P"; assemblyName = null; // 无需输出程序集名 return; } // 其他类型默认输出类名 typeName = serializedType.Name; assemblyName = null; } // 反序列化时映射鉴别器名称到类型 public Type BindToType(string assemblyName, string typeName) { return typeName switch { "P" => typeof(Person), "Adult" => typeof(Adult), // 可扩展其他类型映射 _ => Type.GetType($"{typeName}, {Assembly.GetExecutingAssembly().FullName}") }; } }
- 自定义转换器控制鉴别器输出
public class PersonNewtonsoftConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(Person).IsAssignableFrom(objectType); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var jo = JObject.FromObject(value); // 仅为Person类型添加__type字段 if (value.GetType() == typeof(Person)) { jo.AddFirst(new JProperty("__type", "P")); } jo.WriteTo(writer); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jo = JObject.Load(reader); if (jo.TryGetValue("__type", out var typeToken)) { var targetType = typeToken.Value<string>() switch { "P" => typeof(Person), _ => typeof(Adult) }; return jo.ToObject(targetType, serializer); } return jo.ToObject<Adult>(serializer); } }
- 配置并执行序列化
var settings = new JsonSerializerSettings { SerializationBinder = new CustomSerializationBinder(), Converters = { new PersonNewtonsoftConverter() }, // 可添加其他配置如格式化输出等 }; var people = new List<Person> { new Person { Name = "AbcPerson" }, new Adult { Name = "XyzPerson", Occupation = "" } }; var json = JsonConvert.SerializeObject(people, settings); // 输出结果:[{"__type":"P","Name":"AbcPerson"},{"Occupation":"","Name":"XyzPerson"}]
内容的提问来源于stack exchange,提问作者Yola
相关产品推荐
相关产品推荐

