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

如何将多个同类型对象序列化到同一XML文件并反序列化?

解决XML多对象序列化/反序列化的根元素异常问题

问题原因

你遇到的System.InvalidOperationException(内部异常XmlException: There are multiple root elements),本质是XML语法要求整个文档只能有一个根节点。当前代码直接将多个<Person>节点拼接在同一个文件中,导致文件结构不符合XML规范,反序列化时解析器无法处理。

解决方案

针对你需要逐个写入数千个对象、无法在内存中累积的场景,提供两种可行方案:

方案一:为所有对象添加根容器

通过创建容器类包裹所有Person对象,让文件符合标准XML结构,同时支持逐个写入对象。

步骤1:定义容器类

[XmlRoot("People")]
public class PeopleContainer : List<Person> { }

步骤2:修改序列化逻辑

第一次创建文件时写入根节点开始标签,之后每次追加对象时直接序列化单个Person,所有对象写入完成后追加根节点结束标签:

// 第一次创建文件并写入第一个对象
using (var stream = new FileStream("people.xml", FileMode.Create))
using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true }))
{
    writer.WriteStartElement("People"); // 写入根节点开始
    var serializer = new XmlSerializer(typeof(Person));
    Person jacob = new Person { Name = "Jacob", Age = 33, Alive = true };
    serializer.Serialize(writer, jacob);
}

// 追加第二个对象(后续批量写入逻辑相同)
using (var stream = new FileStream("people.xml", FileMode.Append))
using (var writer = XmlWriter.Create(stream, new XmlWriterSettings 
{ 
    Indent = true, 
    OmitXmlDeclaration = true, 
    ConformanceLevel = ConformanceLevel.Auto 
}))
{
    var serializer = new XmlSerializer(typeof(Person));
    Person rebecca = new Person { Name = "Rebecca", Age = 45, Alive = true };
    serializer.Serialize(writer, rebecca);
}

// 所有对象写入完成后,追加根节点结束标签
using (var stream = new StreamWriter("people.xml", true))
{
    stream.WriteLine("</People>");
}

步骤3:修改反序列化逻辑

用XmlReader逐个读取子节点:

List<Person> people = new List<Person>();
var serializer = new XmlSerializer(typeof(Person));
using (var stream = new FileStream("people.xml", FileMode.Open))
using (var reader = XmlReader.Create(stream))
{
    // 定位到第一个Person节点
    if (reader.ReadToDescendant("Person"))
    {
        do
        {
            people.Add((Person)serializer.Deserialize(reader));
        } while (reader.ReadToNextSibling("Person"));
    }
}

方案二:使用XML片段模式处理

直接将每个对象序列化为XML片段(不带完整文档结构),反序列化时以片段模式读取,无需额外容器类。

步骤1:修改序列化方法

public static void SerializeToFragment<T>(Stream stream, T value)
{
    var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
    var serializer = new XmlSerializer(typeof(T));
    var settings = new XmlWriterSettings
    {
        Indent = true,
        OmitXmlDeclaration = true,
        ConformanceLevel = ConformanceLevel.Fragment // 启用片段模式
    };
    using (var writer = XmlWriter.Create(stream, settings))
    {
        serializer.Serialize(writer, value, emptyNamespaces);
    }
}

步骤2:批量写入对象

// 第一个对象(创建文件)
using (var stream = new FileStream("people.xml", FileMode.Create))
{
    Person jacob = new Person { Name = "Jacob", Age = 33, Alive = true };
    SerializeToFragment(stream, jacob);
}

// 追加后续对象
using (var stream = new FileStream("people.xml", FileMode.Append))
{
    Person rebecca = new Person { Name = "Rebecca", Age = 45, Alive = true };
    SerializeToFragment(stream, rebecca);
}

步骤3:片段模式反序列化

List<Person> people = new List<Person>();
var serializer = new XmlSerializer(typeof(Person));
var readerSettings = new XmlReaderSettings
{
    ConformanceLevel = ConformanceLevel.Fragment // 片段模式读取
};

using (var stream = new FileStream("people.xml", FileMode.Open))
using (var reader = XmlReader.Create(stream, readerSettings))
{
    while (reader.Read())
    {
        // 只处理Person元素节点
        if (reader.NodeType == XmlNodeType.Element && reader.Name == "Person")
        {
            people.Add((Person)serializer.Deserialize(reader));
        }
    }
}

方案对比

  • 方案一:符合标准XML结构,兼容性强,适合需要用第三方XML工具处理文件的场景,但需确保最终写入根节点结束标签。
  • 方案二:无需额外容器类,写入逻辑更简洁,适合大批量逐个写入的场景,但部分严格的XML解析器可能不支持片段格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 00:09:23