C#中的XDocument是操作XML最常用的类之一,它属于LINQ to XML体系,加载、查询、修改XML都非常方便。但不少人在保存文件时发现,生成的XML内容要么全部挤在一行,要么缩进方式不符合要求,甚至编码出现乱码。其实XDocument本身自带格式化能力,关键在于正确使用Save方法的重载和SaveOptions枚举,必要时再配合XmlWriterSettings做更精细的控制。本文将系统讲解这些设置方法。

XDocument.Save的默认行为与SaveOptions枚举
先看一个最基础的保存示例。下面的代码构造了一个简单的XML文档,然后直接保存到文件:
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("Root",
new XElement("Child", "内容1"),
new XElement("Child", "内容2")
)
);
doc.Save("output.xml");
直接调用Save(string fileName)这个重载时,XDocument默认会对XML进行格式化:每个元素独占一行,子元素相对父元素缩进两个空格。这与XDocument.ToString()的默认行为一致。所以如果你发现保存出来的文件没有缩进,很可能是某些调用方式改变了默认行为。
Save方法有一个接受SaveOptions枚举参数的重载,这个枚举有两个常用值。第一个是SaveOptions.DisableFormatting,加上它之后保存的XML会去除所有无关的空白,整个文档压成一行,适合对文件体积敏感或机器读取的场景:
doc.Save("output.xml", SaveOptions.DisableFormatting);
第二个是SaveOptions.OmitDuplicateNamespaces,它会在序列化时省略重复的命名空间声明,输出结果更简洁。两个选项可以用按位或组合使用。注意SaveOptions.None就是默认的格式化保存,也就是带换行和两空格缩进的效果。
使用XmlWriterSettings精确控制缩进字符与换行符
默认的两空格缩进虽然够用,但在很多场景下不够灵活。比如团队规范要求四个空格缩进,或者需要生成制表符缩进、指定换行符是\n还是\r\n。这时就需要用到XmlWriterSettings配合XmlWriter.Create来保存:
XDocument doc = XDocument.Load("input.xml");
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true, // 开启缩进
IndentChars = " ", // 每层缩进4个空格,也可设为 "\t"
NewLineChars = "\r\n", // 换行符,Linux下可改为 "\n"
NewLineHandling = NewLineHandling.Replace,
Encoding = new UTF8Encoding(false) // 无BOM的UTF-8
};
using (XmlWriter writer = XmlWriter.Create("formatted.xml", settings))
{
doc.Save(writer);
}
这段代码中有几个关键点需要理解。Indent属性决定是否缩进,设为true相当于默认的格式化模式;IndentChars指定每一层缩进使用的字符串,可以是任意字符串,最常见的是四个空格或制表符;NewLineChars控制换行符的形式,Windows平台默认是\r\n,跨平台场景下经常需要手动统一。
还要特别提醒一点:当XML是从外部文件加载进来时,如果原始文件本身带有缩进空白,这些空白会作为文本节点被保留在XDocument中。此时即使开启格式化,输出也可能出现缩进叠加、层次错乱的问题。解决办法是加载时加上LoadOptions.None或对文档执行doc.Descendants().Attributes(XNamespace.Xml + "space").Remove()之类的清理,更直接的方式是加载后重新构建,或者加载时使用XDocument.Parse(xml, LoadOptions.None)确保没有残留空白节点。
另外,通过XmlWriter保存时,如果文档中没有XDeclaration,输出不会包含<?xml version="1.0"?>声明。如果需要声明,构造XDocument时要显式添加XDeclaration对象。但要注意一个细节:使用XmlWriter保存时,实际编码以XmlWriterSettings中的Encoding为准,XDeclaration中写的编码只是声明文本,两者不一致时以writer为准。
编码、BOM与内存输出的常见陷阱
除了缩进,编码问题是保存XML时另一个高频踩坑点。直接调用doc.Save("file.xml")时,默认写出的是带BOM的UTF-8。有些系统(尤其是与Java或其他平台交互时)不接受BOM,会导致对方解析失败。解决方法就是像上面那样显式指定new UTF8Encoding(false),参数false表示不输出BOM。
有时我们不需要写文件,而是想把格式化后的XML保存到字符串或内存流中,比如存数据库或者通过网络发送。直接用doc.ToString()虽然默认带缩进,但无法控制缩进字符和换行符。正确做法是配合StringBuilder和XmlWriter:
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = "\t",
OmitXmlDeclaration = true // 字符串场景通常省略XML声明
};
using (XmlWriter writer = XmlWriter.Create(sb, settings))
{
doc.Save(writer);
}
string xml = sb.ToString();
OmitXmlDeclaration设为true可以省略XML声明,这在把XML片段嵌入其他文档时很有用。如果输出目标是流,同理使用XmlWriter.Create(stream, settings)即可,编码设置同样生效。
最后总结一下选择建议:简单场景直接用Save(fileName),默认缩进就够用;需要定制缩进字符、换行符或编码时,使用XmlWriter加XmlWriterSettings;追求极致体积或供程序读取时,用SaveOptions.DisableFormatting压成单行。掌握这三种方式,XDocument保存XML的格式化需求基本都能覆盖。
C# XDocumentXML缩进XmlWriter格式化修改时间:2026-09-02 06:20:25