如何让C# XmlArray反序列化替换而非追加List元素?
问题:XmlSerializer反序列化时集合值追加而非替换默认值
我定义了一个ConnectionSettings配置类,在构造函数中初始化了默认的连接超时时间及重连时间序列List。当修改该序列并序列化到文件后,反序列化时文件中的值会追加到构造函数的默认List中,而非替换默认值。如何让XmlSerializer在反序列化时用流中的值替换默认值?
原代码如下:
[XmlRoot("ConnectionSettings")] public class ConnectionSettings { public ConnectionSettings() { ConnectTimeoutInSeconds = 5; ReconnectSequenceInSeconds = new List<int>() { 1, 5, 5, 20, 20, 60 }; } [XmlElement("ConnectTimeoutInSeconds")] public int ConnectTimeoutInSeconds { get; set; } [XmlArray("ReconnectSequence")] [XmlArrayItem("ReconnectTimeInSeconds")] public List<int> ReconnectSequenceInSeconds { get; set; } public void Save(string file) { using (Stream writer = new FileStream(file, FileMode.Create)) { var serializer = new XmlSerializer(typeof(ConnectionSettings)); serializer.Serialize(writer, this); } } public static ConnectionSettings Load(string file) { using (Stream reader = new FileStream(file, FileMode.Open, FileAccess.Read)) { var serializer = new XmlSerializer(typeof(ConnectionSettings)); return serializer.Deserialize(reader) as ConnectionSettings; } } }
测试代码:
static void SaveAndLoad(string file) { var settings = new ConnectionSettings() { ConnectTimeoutInSeconds = 20, ReconnectSequenceInSeconds = new List<int>() { 30, 45, 60 } }; Console.WriteLine("期望结果: " + string.Join(",", settings.ReconnectSequenceInSeconds)); settings.Save(file); var loadedSettings = ConnectionSettings.Load(file); Console.WriteLine("实际结果: " + string.Join(",", loadedSettings.ReconnectSequenceInSeconds)); }
测试输出:
期望结果: 30,45,60 实际结果: 1,5,5,20,20,60,30,45,60
原因分析
XmlSerializer处理集合类型属性时,会先调用构造函数创建对象(此时集合已被初始化并填充默认值),然后将反序列化得到的元素逐个添加到已有的集合实例中,而非替换整个集合引用,最终导致默认值与文件值叠加。
解决方案
方案一:拆分默认值初始化逻辑
构造函数中仅初始化集合引用,不填充默认值;将默认值设置逻辑放到单独方法中,在新建实例或反序列化后按需调用:
[XmlRoot("ConnectionSettings")] public class ConnectionSettings { public ConnectionSettings() { ConnectTimeoutInSeconds = 5; // 仅初始化集合引用,不填充默认值 ReconnectSequenceInSeconds = new List<int>(); // 新建实例时初始化默认值 InitializeDefaults(); } [XmlElement("ConnectTimeoutInSeconds")] public int ConnectTimeoutInSeconds { get; set; } [XmlArray("ReconnectSequence")] [XmlArrayItem("ReconnectTimeInSeconds")] public List<int> ReconnectSequenceInSeconds { get; set; } // 初始化默认值的方法 private void InitializeDefaults() { if (!ReconnectSequenceInSeconds.Any()) { ReconnectSequenceInSeconds.AddRange(new List<int>() { 1, 5, 5, 20, 20, 60 }); } } public void Save(string file) { using (Stream writer = new FileStream(file, FileMode.Create)) { var serializer = new XmlSerializer(typeof(ConnectionSettings)); serializer.Serialize(writer, this); } } public static ConnectionSettings Load(string file) { using (Stream reader = new FileStream(file, FileMode.Open, FileAccess.Read)) { var serializer = new XmlSerializer(typeof(ConnectionSettings)); var settings = serializer.Deserialize(reader) as ConnectionSettings; // 反序列化后检查集合,为空则补充默认值 settings?.InitializeDefaults(); return settings; } } }
方案二:实现IXmlSerializable接口手动控制序列化
通过实现IXmlSerializable接口,完全掌控序列化和反序列化流程,直接用反序列化得到的集合替换原有引用:
[XmlRoot("ConnectionSettings")] public class ConnectionSettings : IXmlSerializable { public ConnectionSettings() { ConnectTimeoutInSeconds = 5; ReconnectSequenceInSeconds = new List<int>() { 1, 5, 5, 20, 20, 60 }; } public int ConnectTimeoutInSeconds { get; set; } public List<int> ReconnectSequenceInSeconds { get; set; } public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "ConnectionSettings") { // 读取连接超时时间 if (reader.ReadToDescendant("ConnectTimeoutInSeconds")) { ConnectTimeoutInSeconds = int.Parse(reader.ReadElementContentAsString()); } // 读取重连序列并直接替换集合 if (reader.ReadToDescendant("ReconnectSequence")) { var sequence = new List<int>(); if (reader.ReadToDescendant("ReconnectTimeInSeconds")) { do { sequence.Add(int.Parse(reader.ReadElementContentAsString())); } while (reader.ReadToNextSibling("ReconnectTimeInSeconds")); } ReconnectSequenceInSeconds = sequence; } } } public void WriteXml(System.Xml.XmlWriter writer) { // 写入连接超时时间 writer.WriteElementString("ConnectTimeoutInSeconds", ConnectTimeoutInSeconds.ToString()); // 写入重连序列 writer.WriteStartElement("ReconnectSequence"); foreach (var time in ReconnectSequenceInSeconds) { writer.WriteElementString("ReconnectTimeInSeconds", time.ToString()); } writer.WriteEndElement(); } public void Save(string file) { using (Stream writer = new FileStream(file, FileMode.Create)) { var serializer = new XmlSerializer(typeof(ConnectionSettings)); serializer.Serialize(writer, this); } } public static ConnectionSettings Load(string file) { using (Stream reader = new FileStream(file, FileMode.Open, FileAccess.Read)) { var serializer = new XmlSerializer(typeof(ConnectionSettings)); return serializer.Deserialize(reader) as ConnectionSettings; } } }
验证效果
运行原测试方法SaveAndLoad,此时实际结果将与期望结果一致:
期望结果: 30,45,60 实际结果: 30,45,60
内容的提问来源于stack exchange,提问作者Pete Magsig
相关产品推荐
相关产品推荐

