.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
相关产品推荐
相关产品推荐

