如何用单个XmlWriter序列化多个带缩进的XML片段?
复用XmlWriter序列化多XML片段:解决缩进、换行、无命名空间问题
核心需求回顾
需要用XmlSerializer批量序列化10万+对象为独立XML片段,要求:
- 片段带缩进
- 无默认命名空间
- 无XML序言(
<?xml ...?>) - 换行符为
\n而非默认\r\n - 复用单个
XmlWriter实例以避免重复创建的性能开销
自定义XmlWriter控制换行与缩进
原生XmlTextWriter的换行符默认是\r\n且无法直接修改,我们通过继承子类重写NewLine属性,同时预设缩进配置:
public class IndentedFragmentXmlWriter : XmlTextWriter { public IndentedFragmentXmlWriter(Stream stream, Encoding encoding) : base(stream, encoding) { Indent = true; IndentChars = " "; // 可替换为\t或其他缩进符 NewLine = "\n"; // 指定换行符为\n // 可选:设置为Fragment模式,严格支持多顶级元素 ConformanceLevel = ConformanceLevel.Fragment; } }
批量序列化实现(复用XmlWriter)
结合XmlSerializerNamespaces去掉默认命名空间,同时避免XmlSerializer自动写入XML序言与文档起始节点,确保可重复序列化:
// 示例实体类 public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } // 模拟10万条数据 var largeDataset = Enumerable.Range(1, 100000) .Select(i => new Product { Id = i, Name = $"Product_{i}", Price = i * 1.99m }); // 复用XmlWriter写入文件(推荐用FileStream避免内存溢出) using (var fileStream = new FileStream("output.xml", FileMode.Create, FileAccess.Write)) using (var xmlWriter = new IndentedFragmentXmlWriter(fileStream, new UTF8Encoding(false))) { var serializer = new XmlSerializer(typeof(Product)); var emptyNamespaces = new XmlSerializerNamespaces(); emptyNamespaces.Add("", ""); // 移除默认命名空间 foreach (var item in largeDataset) { // 序列化单个对象为XML片段,无命名空间、无序言 serializer.Serialize(xmlWriter, item, emptyNamespaces); // 可选:在片段之间添加换行,提升可读性 xmlWriter.WriteWhitespace("\n"); } }
关键问题解决说明
- 换行符问题:通过自定义
XmlTextWriter重写NewLine为\n,彻底替换原生的\r\n,同时保留缩进功能。 - Fragment模式异常:设置
ConformanceLevel.Fragment后,通过XmlSerializerNamespaces避免序列化时触发WriteStartDocument调用,同时自定义Writer的缩进配置依然生效。 - 重复序列化异常:由于始终未调用
WriteStartDocument,XmlWriter不会进入"文档结束"状态,可循环序列化任意数量的对象,完美复用实例。
注意事项
- 处理10万级数据时,务必使用
FileStream直接写入,避免用MemoryStream导致内存占用过高。 - 若不需要严格的片段模式,可移除
ConformanceLevel = ConformanceLevel.Fragment,使用默认的Auto模式也能正常工作。 - 缩进符可根据需求修改为
"\t"或多个空格。
内容的提问来源于stack exchange,提问作者Dmitry Avtonomov
相关产品推荐
相关产品推荐

