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

如何让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 20:15:43