C#如何反序列化包含整数与整数范围的混合JSON数组?
解决混合类型JSON数组的反序列化问题
你遇到的核心问题有两个:
- 目标JSON的外层结构是包含
retention数组的对象,直接反序列化成RelayRetention会因为结构不匹配失败; kinds数组是混合类型(单个整数和整数子数组),无法用int[]或int[][]直接接收。
以下是两种可行的解决方案:
方案一:使用JsonElement手动解析(快速实现)
这种方法无需自定义转换器,先接收原始JSON元素再手动处理类型判断:
修正类结构
// 根类:匹配JSON外层的retention数组 public class RootRetention { [JsonPropertyName("retention")] public List<RelayRetention> Retention { get; set; } = new(); } public class RelayRetention { public override string ToString() { var kindsDesc = new List<string>(); if (Kinds.HasValue) { foreach (var element in Kinds.Value.EnumerateArray()) { // 判断元素是单个数字还是数组 if (element.ValueKind == JsonValueKind.Number) { kindsDesc.Add(element.GetInt32().ToString()); } else if (element.ValueKind == JsonValueKind.Array) { var range = element.EnumerateArray().Select(e => e.GetInt32()).ToArray(); kindsDesc.Add($"{range[0]}-{range[1]}"); } } } return $"Time: {Time}, Count: {Count}, Kinds: {string.Join(", ", kindsDesc)}"; } [JsonPropertyName("kinds")] public JsonElement? Kinds { get; set; } [JsonPropertyName("time")] public int? Time { get; set; } [JsonPropertyName("count")] public int? Count { get; set; } }
反序列化代码
string s = @"{""retention"": [ { ""kinds"": [0, 1, [5, 7], [40, 49]], ""time"": 3600, ""count"": 10000 } ]}"; var root = JsonSerializer.Deserialize<RootRetention>(s); // 测试输出 if (root?.Retention?.Count > 0) { Console.WriteLine(root.Retention[0].ToString()); // 输出:Time: 3600, Count: 10000, Kinds: 0, 1, 5-7, 40-49 }
方案二:自定义JsonConverter(面向对象实现)
如果需要更规范的类型封装,或者要支持序列化回原格式,可以自定义转换器处理混合类型:
定义KindItem封装类
public class KindItem { // 单个值 public int? SingleValue { get; set; } // 范围(Start和End) public (int Start, int End)? Range { get; set; } // 工厂方法创建实例 public static KindItem FromSingle(int value) => new() { SingleValue = value }; public static KindItem FromRange(int start, int end) => new() { Range = (start, end) }; }
自定义JsonConverter
public class KindItemConverter : JsonConverter<List<KindItem>> { public override List<KindItem> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var items = new List<KindItem>(); if (reader.TokenType != JsonTokenType.StartArray) { throw new JsonException("kinds字段应为数组格式"); } while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndArray) break; if (reader.TokenType == JsonTokenType.Number) { items.Add(KindItem.FromSingle(reader.GetInt32())); } else if (reader.TokenType == JsonTokenType.StartArray) { var rangeValues = new List<int>(); while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { if (reader.TokenType == JsonTokenType.Number) { rangeValues.Add(reader.GetInt32()); } } if (rangeValues.Count == 2) { items.Add(KindItem.FromRange(rangeValues[0], rangeValues[1])); } else { throw new JsonException("kinds中的范围数组必须包含两个整数"); } } else { throw new JsonException($"kinds中不支持的元素类型:{reader.TokenType}"); } } return items; } // 支持序列化回原JSON格式 public override void Write(Utf8JsonWriter writer, List<KindItem> value, JsonSerializerOptions options) { writer.WriteStartArray(); foreach (var item in value) { if (item.SingleValue.HasValue) { writer.WriteNumberValue(item.SingleValue.Value); } else if (item.Range.HasValue) { writer.WriteStartArray(); writer.WriteNumberValue(item.Range.Value.Start); writer.WriteNumberValue(item.Range.Value.End); writer.WriteEndArray(); } } writer.WriteEndArray(); } }
修正RelayRetention类并使用转换器
public class RootRetention { [JsonPropertyName("retention")] public List<RelayRetention> Retention { get; set; } = new(); } public class RelayRetention { public override string ToString() { var kindsDesc = new List<string>(); if (Kinds != null) { foreach (var item in Kinds) { if (item.SingleValue.HasValue) { kindsDesc.Add(item.SingleValue.Value.ToString()); } else if (item.Range.HasValue) { kindsDesc.Add($"{item.Range.Value.Start}-{item.Range.Value.End}"); } } } return $"Time: {Time}, Count: {Count}, Kinds: {string.Join(", ", kindsDesc)}"; } [JsonPropertyName("kinds")] [JsonConverter(typeof(KindItemConverter))] public List<KindItem>? Kinds { get; set; } [JsonPropertyName("time")] public int? Time { get; set; } [JsonPropertyName("count")] public int? Count { get; set; } }
反序列化代码
string s = @"{""retention"": [ { ""kinds"": [0, 1, [5, 7], [40, 49]], ""time"": 3600, ""count"": 10000 } ]}"; var root = JsonSerializer.Deserialize<RootRetention>(s); // 测试输出 if (root?.Retention?.Count > 0) { Console.WriteLine(root.Retention[0].ToString()); } // 测试序列化回原格式 var json = JsonSerializer.Serialize(root); Console.WriteLine(json);
方案对比
- 方案一:实现简单,无需额外代码,适合仅需要读取数据的场景;
- 方案二:类型封装更清晰,支持双向序列化(读/写),适合需要对
kinds数据进行业务处理的场景。
内容的提问来源于stack exchange,提问作者red line
相关产品推荐
相关产品推荐

