使用Newtonsoft.Json反序列化字符串数组与二维字符串数组的混合负载
解决方案:自定义JsonConverter处理混合数组类型反序列化
1. 完善SingleOrArray<T>类实现
先补全SingleOrArray<T>的ICollection<T>接口实现,确保序列化和反序列化基础功能正常:
public class SingleOrArray<T> : ICollection<T> { public bool IsArray { get; set; } = false; public T Value { get; set; } public T[] Values => _items.ToArray(); private readonly List<T> _items = new(); // ICollection<T> 接口实现 public int Count => _items.Count; public bool IsReadOnly => false; public void Add(T item) => _items.Add(item); public void Clear() => _items.Clear(); public bool Contains(T item) => _items.Contains(item); public void CopyTo(T[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex); public bool Remove(T item) => _items.Remove(item); public IEnumerator<T> GetEnumerator() => _items.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); // 辅助方法:从单个值初始化 public static SingleOrArray<T> FromSingleValue(T value) { return new SingleOrArray<T> { IsArray = false, Value = value }; } // 辅助方法:从集合初始化 public static SingleOrArray<T> FromCollection(IEnumerable<T> collection) { var instance = new SingleOrArray<T> { IsArray = true }; foreach (var item in collection) { instance.Add(item); } return instance; } }
2. 实现自定义JsonConverter
创建SingleOrArrayListConverter<T>,处理List<SingleOrArray<T>>的反序列化逻辑,自动判断JSON是单个数组还是数组的数组:
public class SingleOrArrayListConverter<T> : JsonConverter<List<SingleOrArray<T>>> { public override List<SingleOrArray<T>> ReadJson(JsonReader reader, Type objectType, List<SingleOrArray<T>> existingValue, bool hasExistingValue, JsonSerializer serializer) { var token = JToken.Load(reader); var result = new List<SingleOrArray<T>>(); switch (token.Type) { // 情况1:JSON是单个数组(如 ["foo", "bar"]),将其作为单个SingleOrArray<T>的Value case JTokenType.Array when token.Children().All(c => c.Type != JTokenType.Array): var singleValue = token.ToObject<T>(); result.Add(SingleOrArray<T>.FromSingleValue(singleValue)); break; // 情况2:JSON是数组的数组(如 [["foo","bar"], ["baz","qux"]]),每个子数组作为一个SingleOrArray<T>的Value case JTokenType.Array when token.Children().All(c => c.Type == JTokenType.Array): foreach (var childToken in token.Children<JArray>()) { var value = childToken.ToObject<T>(); result.Add(SingleOrArray<T>.FromSingleValue(value)); } break; default: throw new JsonSerializationException("无法识别的JSON格式,仅支持数组或数组的数组"); } return result; } public override void WriteJson(JsonWriter writer, List<SingleOrArray<T>> value, JsonSerializer serializer) { // 序列化逻辑:如果列表只有一个元素且是单个值,直接序列化该值;否则序列化所有元素的Value组成的数组 if (value.Count == 1 && !value[0].IsArray) { serializer.Serialize(writer, value[0].Value); } else { writer.WriteStartArray(); foreach (var item in value) { serializer.Serialize(writer, item.IsArray ? item.Values : item.Value); } writer.WriteEndArray(); } } }
3. 定义反序列化对象
调整MyObject类,绑定自定义转换器:
public class MyObject { [JsonConverter(typeof(SingleOrArrayListConverter<string[]>))] public List<SingleOrArray<string[]>> Values { get; set; } }
4. 适配string[]场景的单元测试
修改测试用例,验证两种JSON格式的反序列化结果:
public class MyObjectTests { [Fact] public void SingleArrayTest() { // Arrange var testData = "{ \"Values\": [ \"foo\", \"bar\" ] }"; // Act var target = JsonConvert.DeserializeObject<MyObject>(testData); // Assert Assert.NotNull(target); Assert.Single(target.Values); var singleItem = target.Values[0]; Assert.False(singleItem.IsArray); Assert.Equal(new[] { "foo", "bar" }, singleItem.Value); } [Fact] public void ArrayOfArraysTest() { // Arrange var testData = "{ \"Values\": [ [ \"foo\", \"bar\" ], [ \"baz\", \"qux\" ] ] }"; // Act var target = JsonConvert.DeserializeObject<MyObject>(testData); // Assert Assert.NotNull(target); Assert.Equal(2, target.Values.Count); var firstItem = target.Values[0]; Assert.False(firstItem.IsArray); Assert.Equal(new[] { "foo", "bar" }, firstItem.Value); var secondItem = target.Values[1]; Assert.False(secondItem.IsArray); Assert.Equal(new[] { "baz", "qux" }, secondItem.Value); } }
关键说明
- 转换器自动识别JSON结构:
- 单个数组(如
["foo","bar"])会被包装为一个SingleOrArray<string[]>,IsArray=false,Value为该字符串数组 - 数组的数组(如
[["foo","bar"],["baz","qux"]])会拆分为多个SingleOrArray<string[]>元素,每个元素的Value对应子数组
- 单个数组(如
- 忽略混合类型JSON(如
["foo",["bar"]]),遇到时抛出序列化异常
内容的提问来源于stack exchange,提问作者devlord
相关产品推荐
相关产品推荐

