如何将多个同类型对象序列化到同一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
相关产品推荐
相关产品推荐

