C# XmlSerializer反序列化含非空校验列表属性对象的解决方案
问题根因
XmlSerializer 反序列化集合类型属性时的固定处理逻辑是:先创建一个空的对应集合实例赋值给属性,再逐个读取XML中的集合元素调用Add方法填充到集合里。你原有Authors属性的setter会校验集合不能为空,所以赋值空列表的步骤就会直接抛出异常,导致反序列化中断。
可行解决方案
方案1:代理属性对接序列化(你最终选用的方案,兼容性最优)
原理是将原有带业务校验的Authors属性标记为序列化忽略,新增一个数组类型的代理属性专门对接序列化逻辑,数组类型在反序列化时会先读取完全部元素再赋值,不会触发空集合校验:
[XmlIgnore] [JsonIgnore] [IgnoreDataMember] public List<string> Authors { get => new(_authors); set { if (value == null) throw new ArgumentNullException(nameof(value)); _authors = value.Any() ? value : throw new ArgumentOutOfRangeException(nameof(value), @"Must have at least one author."); } } [XmlArray(nameof(Authors))] [JsonProperty(nameof(Authors))] public string[] AuthorsArray { get => Authors.ToArray(); set { if (value == null || !value.Any()) return; if (_authors == null) { Authors = new List<string>(value); } else { Authors.AddRange(value); } } }
这个方案的优势:
- 完全保留原有
Authors属性的业务校验逻辑,业务层调用无感知 - 同时兼容XML、JSON等多种序列化框架,不需要单独适配
Authors的get方法返回新的集合副本,避免外部代码直接修改内部私有字段_authors的状态,保证了封装性
方案2:自定义序列化逻辑
实现IXmlSerializable接口自己控制Book类的序列化/反序列化流程,在反序列化时先读完所有Authors节点的内容,再赋值给Authors属性触发校验。这个方案灵活性高,但编码成本较高,需要自己处理所有字段的序列化逻辑。
方案3:调整校验逻辑的触发时机
修改Authors的setter,仅在非反序列化场景下做空列表校验,比如增加一个标记位,反序列化时临时关闭校验,反序列化完成后再统一校验。这个方案侵入性较强,容易出现校验遗漏的问题。
反序列化代码适配
你现有反序列化代码不需要做任何修改,代理属性会自动适配你提供的XML节点结构:
using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); var xmlSerializer = new XmlSerializer(typeof(List<Book>)); var data = (List<Book>)xmlSerializer.Deserialize(fileStream);
内容的提问来源于stack exchange,提问作者Pavel Antropov
相关产品推荐
相关产品推荐

