C# Newtonsoft.Json如何实现接口与字符串双格式JSON序列化反序列化
解决方案
针对Newtonsoft.Json下IHello类型属性的双格式序列化/反序列化需求,不需要修改原有类结构、不需要向使用者暴露泛型参数,通过自定义JsonConverter即可完全实现需求,对上层使用者无感知。
核心实现:自定义JSON转换器
实现JsonConverter<IHello>类,同时兼容两种格式的序列化、反序列化逻辑:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; public class HelloInterfaceConverter : JsonConverter<IHello> { private readonly bool _serializeAsString; /// <summary> /// 初始化转换器 /// </summary> /// <param name="serializeAsString">true时序列化为简化字符串格式,false时保留原有对象格式</param> public HelloInterfaceConverter(bool serializeAsString = true) { _serializeAsString = serializeAsString; } public override IHello ReadJson(JsonReader reader, Type objectType, IHello existingValue, bool hasExistingValue, JsonSerializer serializer) { JToken token = JToken.Load(reader); // 识别字符串格式:{"Message":"Yo!"} if (token.Type == JTokenType.String) { return new Hello1 { Phrase = token.Value<string>() }; } // 识别原有对象格式:{"Message":{"Phrase":"Yo!"}} if (token.Type == JTokenType.Object) { var hello = new Hello1(); serializer.Populate(token.CreateReader(), hello); return hello; } throw new JsonSerializationException("Message字段格式不合法"); } public override void WriteJson(JsonWriter writer, IHello value, JsonSerializer serializer) { if (value == null) { writer.WriteNull(); return; } // 根据配置决定输出格式 if (_serializeAsString) { writer.WriteValue(value.Phrase); } else { JObject.FromObject(value, serializer).WriteTo(writer); } } }
接入方式
原有Greeting类不需要改成泛型实现,保留原有定义即可,两种接入方式可选:
- 方式1:给属性打特性标记,固定序列化规则
public class Greeting { [JsonConverter(typeof(HelloInterfaceConverter))] public IHello Message { get; set; } }
- 方式2:全局/单次序列化时传入配置,不需要修改实体类,可动态切换输出格式
// 序列化为简化字符串格式 var stringSettings = new JsonSerializerSettings { Converters = { new HelloInterfaceConverter(serializeAsString: true) } }; var jsonText = JsonConvert.SerializeObject(greeting, stringSettings); // 输出:{"Message":"Yo!"} // 序列化为原有对象格式 var objectSettings = new JsonSerializerSettings { Converters = { new HelloInterfaceConverter(serializeAsString: false) } }; var jsonText2 = JsonConvert.SerializeObject(greeting, objectSettings); // 输出:{"Message":{"Phrase":"Yo!"}} // 反序列化时两种格式都可自动识别,不需要额外配置 var g1 = JsonConvert.DeserializeObject<Greeting>("{\"Message\":\"Yo!\"}", stringSettings); var g2 = JsonConvert.DeserializeObject<Greeting>("{\"Message\":{\"Phrase\":\"Yo!\"}}", stringSettings);
方案优势
- 上层使用者完全不需要感知底层处理逻辑,和原来一样实例化
Greeting类即可,不需要手动指定泛型参数 - 同时兼容两种格式的反序列化,不管传入的是字符串还是对象结构都能正常解析
- 序列化格式可灵活控制,不需要修改业务实体代码
- 后续如果新增
IHello的其他实现类,只需要扩展转换器内的识别逻辑即可,不影响上层调用
内容的提问来源于stack exchange,提问作者ImJoe
相关产品推荐
相关产品推荐

