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

.NET 7中实现IEnumerable后JSON序列化异常问题求助

解决.NET 7中实现IEnumerable的类JSON序列化丢失属性及反序列化失败问题

问题原因

System.Text.Json默认会将实现IEnumerable<T>的类型视为集合类型处理:

  • 序列化时仅输出集合元素,忽略类中的其他属性(比如你的FileHeader);
  • 反序列化时会尝试通过集合的方式实例化对象,但自定义类通常没有符合框架要求的集合构造函数,因此抛出System.NotSupportedException。

解决方案

方案1:显式定义集合属性并委托IEnumerable实现

将集合作为类的内部字段,显式标注所有需要序列化的属性,同时把IEnumerable<T>的实现委托给内部集合:

// 基础元素类
public class NumberRange
{
    public int Start { get; set; }
    public int End { get; set; }
}

// 修改后的JsonNumberRanges类
public class JsonNumberRanges : IEnumerable<NumberRange>
{
    // 显式标注需要序列化的头部属性
    [JsonPropertyName("FileHeader")]
    public string FileHeader { get; set; }

    // 内部存储集合的字段,同样标注序列化名称
    [JsonPropertyName("Ranges")]
    private List<NumberRange> _ranges = new();

    // 实现IEnumerable接口,逻辑委托给内部List
    public IEnumerator<NumberRange> GetEnumerator() => _ranges.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    // 按需添加元素操作方法
    public void Add(NumberRange range) => _ranges.Add(range);
}

这种方式既保留了IEnumerable<T>的遍历特性,又能让所有属性正常参与序列化/反序列化。

方案2:自定义JsonConverter完全控制序列化逻辑

如果需要更灵活的序列化行为,可以编写自定义转换器,手动处理属性和集合的读写:

// 自定义转换器
public class JsonNumberRangesConverter : JsonConverter<JsonNumberRanges>
{
    public override JsonNumberRanges Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var doc = JsonDocument.ParseValue(ref reader);
        var root = doc.RootElement;

        var result = new JsonNumberRanges();
        
        // 读取FileHeader属性
        if (root.TryGetProperty("FileHeader", out var headerElem))
        {
            result.FileHeader = headerElem.GetString();
        }
        
        // 读取并反序列化Ranges数组
        if (root.TryGetProperty("Ranges", out var rangesElem))
        {
            foreach (var rangeElem in rangesElem.EnumerateArray())
            {
                var range = JsonSerializer.Deserialize<NumberRange>(rangeElem.GetRawText(), options);
                result.Add(range);
            }
        }

        return result;
    }

    public override void Write(Utf8JsonWriter writer, JsonNumberRanges value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        
        // 写入FileHeader
        writer.WriteString("FileHeader", value.FileHeader);
        
        // 写入Ranges数组
        writer.WriteStartArray("Ranges");
        foreach (var range in value)
        {
            JsonSerializer.Serialize(writer, range, options);
        }
        writer.WriteEndArray();
        
        writer.WriteEndObject();
    }
}

// 在目标类上标注使用自定义转换器
[JsonConverter(typeof(JsonNumberRangesConverter))]
public class JsonNumberRanges : IEnumerable<NumberRange>
{
    public string FileHeader { get; set; }
    private List<NumberRange> _ranges = new();

    public IEnumerator<NumberRange> GetEnumerator() => _ranges.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public void Add(NumberRange range) => _ranges.Add(range);
}

方案3:放弃IEnumerable实现,改用组合模式

如果你的场景不需要直接遍历JsonNumberRanges实例,最便捷的方式是把集合作为类的一个公开属性,去掉IEnumerable<T>的实现:

public class JsonNumberRanges
{
    public string FileHeader { get; set; }
    public List<NumberRange> Ranges { get; set; } = new();
}

这种方式无需额外配置,序列化和反序列化都会自动处理所有属性,唯一的区别是遍历元素时需要通过Ranges属性操作。

关于[JsonObject]特性无效的说明

在.NET 7中,当类实现IEnumerable<T>时,[JsonObject]特性无法覆盖框架默认的集合类型判定逻辑,因此单独使用该特性无法解决问题,必须配合上述方案之一。

内容的提问来源于stack exchange,提问作者Kevin Feige

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 16:35:29