You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#如何反序列化包含整数与整数范围的混合JSON数组?

解决混合类型JSON数组的反序列化问题

你遇到的核心问题有两个:

  1. 目标JSON的外层结构是包含retention数组的对象,直接反序列化成RelayRetention会因为结构不匹配失败;
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 17:40:15