如何在.NET中反序列化可能为标量或数组的JSON属性?
解决JSON标量/数组属性反序列化为C#集合的通用方案
针对JSON属性可能是字符串或字符串数组,需要统一反序列化为IReadOnlyCollection<string>或List<string>的场景,以下是几种低维护成本的解决方案:
一、Json.NET 通用自定义转换器(推荐,兼容现有代码)
编写一个通用转换器,可处理任意类型的标量转集合,无需为每个类单独编写转换器:
public class SingleOrArrayConverter<T> : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(IEnumerable<T>).IsAssignableFrom(objectType) || objectType == typeof(T); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var tokenType = reader.TokenType; List<T> itemList; if (tokenType == JsonToken.StartArray) { itemList = serializer.Deserialize<List<T>>(reader); } else { var singleItem = serializer.Deserialize<T>(reader); itemList = new List<T> { singleItem }; } // 根据目标类型返回对应集合实例 if (objectType.IsInterface) { switch (objectType) { case Type t when t == typeof(IReadOnlyCollection<T>): return itemList.AsReadOnly(); case Type t when t == typeof(IEnumerable<T>) || t == typeof(IList<T>): return itemList; default: throw new JsonSerializationException($"不支持转换为接口类型: {objectType}"); } } else if (objectType == typeof(List<T>)) { return itemList; } throw new JsonSerializationException($"无法转换为目标类型: {objectType}"); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 序列化时默认输出数组,若需单元素输出标量可自行修改逻辑 serializer.Serialize(writer, value); } }
使用方式
- 全局生效:注册到序列化设置,所有符合条件的属性自动处理
var settings = new JsonSerializerSettings(); settings.Converters.Add(new SingleOrArrayConverter<string>()); var myObj = JsonConvert.DeserializeObject<MyType>(jsonStr, settings);
- 单个属性生效:仅对指定属性应用转换器
public record MyType( [JsonConverter(typeof(SingleOrArrayConverter<string>))] IReadOnlyCollection<string> MyProperty); // 或传统类写法 public class MyType2 { [JsonConverter(typeof(SingleOrArrayConverter<string>))] public List<string>? MyProperty { get; set; } }
二、System.Text.Json 通用转换器工厂(原生无依赖)
若愿意切换到.NET内置的System.Text.Json,可通过转换器工厂实现全局自动处理:
public class SingleOrArrayConverterFactory : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) { return typeToConvert.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(typeToConvert.GetGenericTypeDefinition()) && typeToConvert.GetGenericArguments().Length == 1; } public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var elementType = typeToConvert.GetGenericArguments()[0]; var converterType = typeof(SingleOrArrayConverter<>).MakeGenericType(elementType); return (JsonConverter)Activator.CreateInstance(converterType)!; } private class SingleOrArrayConverter<T> : JsonConverter<IEnumerable<T>> { public override IEnumerable<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.StartArray) { return JsonSerializer.Deserialize<List<T>>(ref reader, options)!; } else { var singleItem = JsonSerializer.Deserialize<T>(ref reader, options)!; return new List<T> { singleItem }; } } public override void Write(Utf8JsonWriter writer, IEnumerable<T> value, JsonSerializerOptions options) { var itemList = value.ToList(); if (itemList.Count == 1) { JsonSerializer.Serialize(writer, itemList[0], options); } else { JsonSerializer.Serialize(writer, itemList, options); } } } }
使用方式
var options = new JsonSerializerOptions(); options.Converters.Add(new SingleOrArrayConverterFactory()); var myObj = JsonSerializer.Deserialize<MyType>(jsonStr, options);
三、高性能选项:Utf8Json
若追求极致性能,可使用第三方库Utf8Json,通过通用格式化器处理:
public class SingleOrArrayFormatter<T> : IJsonFormatter<IEnumerable<T>> { public void Serialize(ref JsonWriter writer, IEnumerable<T> value, IJsonFormatterResolver formatterResolver) { var itemList = value.ToList(); if (itemList.Count == 1) { formatterResolver.GetFormatter<T>().Serialize(ref writer, itemList[0], formatterResolver); } else { writer.WriteBeginArray(); foreach (var item in itemList) { formatterResolver.GetFormatter<T>().Serialize(ref writer, item, formatterResolver); } writer.WriteEndArray(); } } public IEnumerable<T> Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (reader.GetCurrentJsonToken() == JsonToken.BeginArray) { reader.ReadIsBeginArray(); var itemList = new List<T>(); while (reader.GetCurrentJsonToken() != JsonToken.EndArray) { var item = formatterResolver.GetFormatter<T>().Deserialize(ref reader, formatterResolver); itemList.Add(item); reader.Next(); } reader.ReadIsEndArray(); return itemList; } else { var singleItem = formatterResolver.GetFormatter<T>().Deserialize(ref reader, formatterResolver); return new List<T> { singleItem }; } } }
使用方式
var resolver = new CompositeResolver( new[] { new SingleOrArrayFormatter<string>() }, StandardResolver.Default); var myObj = JsonSerializer.Deserialize<MyType>(jsonStr, resolver);
内容的提问来源于stack exchange,提问作者solublefish
相关产品推荐
相关产品推荐

