C# XDocument Save SaveOptions.None 默认保存选项

4次阅读

saveoptions.none会删除格式性空白节点但保留显式字符串;真正保留缩进需加载时用loadoptions.preservewhitespace并保存时用saveoptions.disableformatting或format。

C# XDocument Save SaveOptions.None 默认保存选项

SaveOptions.None 会删除空白节点,但不是所有空白

SaveOptions.NoneXDocument.Save 的默认选项,它会让序列化过程“忽略”某些空白文本节点——但仅限于那些被判定为「格式性空白」(即仅由空格、换行、制表符组成,且父元素有子元素时出现在元素之间)的节点。它不会动你显式写入的 " ""n" 字符串内容。

常见错误现象:xml 文件保存后缩进没了、换行消失了、相邻标签“粘连”在一起,比如 <name>Alice</name><age>30</age> 而不是分行显示。

  • 这是预期行为,不是 bug —— SaveOptions.None 的设计目标就是紧凑输出
  • 如果你用 XElement.Parse("<root>n <a></a>n <b></b>n</root>") 创建树,那些换行和缩进在解析阶段就已被当作格式空白丢弃,SaveOptions.None 只是不额外加回来
  • 真正保留原始缩进需用 SaveOptions.DisableFormatting,但它只对「已存在」的空白节点起作用;如果解析时空白已丢失,保存时也救不回来

想保留换行缩进?别只靠 SaveOptions

仅设置 SaveOptions.Format 不足以让 XML 看起来“美观”。它只控制是否在元素间插入换行和缩进,前提是:树中已有足够的空白节点支撑格式化逻辑。

使用场景:调试输出、生成人可读配置文件、和外部系统做 diff 对比。

  • SaveOptions.Format 会在每个元素开始标签后、结束标签前插入换行+缩进,但不会在文本内容里插空格
  • 若你用 new XElement("root", new XElement("a"), new XElement("b")) 构造,没手动加 XText("n "),那即使选 Format,结果仍是紧凑的
  • 真正可控的方式是:构造时显式插入格式节点,或用 XDocument.ToString(SaveOptions.Format)(注意:ToString() 不走 Save() 流程,行为略有差异)

SaveOptions.DisableFormatting 和 Format 的关键区别

SaveOptions.DisableFormatting 不是“关闭格式化”,而是“禁用自动格式化逻辑”——它原样保留你在 XML 树中手动添加的所有 XText 节点,包括空格、换行、甚至不可见字符。

SaveOptions.Format 是主动注入格式,不管原来有没有。

  • DisableFormatting 前,必须确保你构造树时已经插入了想要的空白,例如:new XElement("root", "n ", new XElement("a"), "n ", new XElement("b"), "n")
  • Format 在保存时动态计算缩进层级,适合快速美化,但无法控制具体缩进宽度(固定为 2 空格)
  • 两者都不影响属性顺序、命名空间声明位置等底层细节;这些由 XDocument 内部状态决定,无法通过 SaveOptions 调整

常见误用:以为 SaveOptions 能修复解析时丢失的空白

很多人试图用 SaveOptions.Format 把从文件读进来的 XML “变好看”,却发现没变化。问题往往出在解析阶段:用 XDocument.Load(path) 默认启用 LoadOptions.PreserveWhitespace 吗?不,默认是 None,也就是丢弃格式空白。

  • 正确做法:加载时明确指定 LoadOptions.PreserveWhitespace,例如:XDocument.Load(path, LoadOptions.PreserveWhitespace)
  • 否则,即使后续用 Format 保存,也只能格式化“现有结构”,没有空白节点可格式化
  • 验证方法:断点查看 doc.Root.Nodes(),里面是否有 XText 类型节点,且 Value 包含换行符

空白处理是分两段的事:加载时留不留,保存时加不加。SaveOptions 只管后半段,前半段得自己把关。

text=ZqhQzanResources