解析修改保存XML时如何保留属性值中>字符不被自动转义
.NET 内置的 XmlDocument、XDocument 搭配默认 XmlWriter 输出时,确实会无条件转义属性和文本中的>字符,这个行为是默认输出逻辑写死的,没有公开的配置开关可以直接关闭,但可以通过以下两种合规的方式实现需求,且不会遗漏必须转义的场景:
方案1:自定义XmlWriter实现标准兼容的转义逻辑(生产环境推荐)
这个方案完全遵循XML 2.4节的转义要求,仅在确实需要转义>的场景(即]]>序列末尾)才输出实体引用,其余场景直接保留原始>字符,不会破坏XML合法性,也不会误改需要保留的格式:- 继承
XmlWriter类,将除WriteString之外的所有抽象成员全部转发给内部包装的原生XmlWriter实例 - 重写
WriteString方法时,对强制转义字符&、<、"、'正常输出对应实体引用 - 针对
>字符维护一个长度为2的滑动窗口,检查当前字符前两个紧邻的字符是否为]]:如果是则转义为>,否则直接输出原始>字符 - 保存XML时,将自定义Writer实例传入
Save方法即可,你对XML文档做的所有结构修改都会正常保留。
核心实现示例:
public class StandardCompliantXmlWriter : XmlWriter { private readonly XmlWriter _inner; private readonly char[] _slidingBuffer = new char[2]; private int _bufIndex = 0; public StandardCompliantXmlWriter(XmlWriter innerWriter) => _inner = innerWriter; public override WriteState WriteState => _inner.WriteState; public override void Flush() => _inner.Flush(); public override void WriteStartElement(string prefix, string localName, string ns) => _inner.WriteStartElement(prefix, localName, string ns); public override void WriteEndElement() => _inner.WriteEndElement(); public override void WriteFullEndElement() => _inner.WriteFullEndElement(); public override void WriteStartAttribute(string prefix, string localName, string ns) => _inner.WriteStartAttribute(prefix, localName, ns); public override void WriteEndAttribute() => _inner.WriteEndAttribute(); public override void WriteString(string text) { if (string.IsNullOrEmpty(text)) return; foreach (var c in text) { switch (c) { case '&': _inner.WriteEntityRef("amp"); break; case '<': _inner.WriteEntityRef("lt"); break; case '"': _inner.WriteEntityRef("quot"); break; case '\'': _inner.WriteEntityRef("apos"); break; case '>': if (_bufIndex >=2 && _slidingBuffer[0] == ']' && _slidingBuffer[1] == ']') _inner.WriteEntityRef("gt"); else _inner.WriteRaw(">"); break; default: _inner.WriteChar(c); break; } _slidingBuffer[_bufIndex % 2] = c; _bufIndex++; } } }调用方式:
var doc = XDocument.Load("yourfile.xml"); // 在此处执行你需要的XML修改逻辑 using var fs = File.Create("output.xml"); using var baseWriter = XmlWriter.Create(fs, new XmlWriterSettings { Indent = true, Encoding = Encoding.UTF8 }); using var writer = new StandardCompliantXmlWriter(baseWriter); doc.Save(writer);输出结果中示例里的
Name="a->b"会原样保留,仅当碰到]]>这类必须转义的序列时才会转义对应的>,完全符合XML标准,也不会出现后处理方案的误替换问题。- 继承
方案2:反射修改内置XmlWriter的私有开关(仅适合固定.NET版本场景)
.NET的原生XmlWriter内部存在一个名为DoNotEscapeRightAngleBracket的私有配置字段,将其设置为true后,原生Writer也会停止非必要的>转义。但这个字段是非公开成员,在不同.NET版本(.NET Framework、.NET Core、.NET 5+)中可能存在命名、位置变化,升级框架时需要重新校验兼容性,不推荐在对稳定性要求高的生产环境使用。
不建议使用输出后替换的方案:全局替换
>为>会破坏必须转义的场景,而基于正则的上下文判断本质上是在重新实现XML解析逻辑,很容易漏掉边界场景引入错误。
内容的提问来源于stack exchange,提问作者Voo

