C# .NET Core下如何快速替换XML属性中未正确转义的<和>符号
C# .NET Core 修复XML未转义尖括号的可行方案
核心前提说明
直接全局替换</>不可行,会破坏合法的XML标签、处理指令、注释等结构的尖括号,必须基于XML上下文判断尖括号是否需要转义。
方案1:使用容错XML解析库(最稳定,兼容任意结构)
推荐使用AngleSharp.Xml库,它原生支持解析非规范XML,自动识别并修复未转义的属性值、文本内容中的尖括号,不需要手动处理不同结构的差异。
实现步骤
- 安装NuGet包:
AngleSharp.Xml - 代码示例:
using AngleSharp; using AngleSharp.Xml.Parser; // 配置解析器,开启容错模式 var context = BrowsingContext.New(Configuration.Default.WithXml()); var parser = context.GetService<IXmlParser>(); var options = new XmlParserOptions { IsSupportingProcessingInstructions = true, IsKeepingSourceReferences = false, // 开启容错,自动修复未转义字符 IsSuppressingErrors = true }; // 读取破损XML var brokenXml = File.ReadAllText("你的XML文件路径"); // 解析并自动修复 var document = parser.ParseDocument(brokenXml, options); // 导出规范XML File.WriteAllText("修复后的输出路径.xml", document.ToXml());
该方案对XML结构无要求,自动处理属性、文本节点内的未转义尖括号,适合绝大多数场景。
方案2:逐字符状态机处理(适合GB级大体积文件,低内存占用)
针对大体积XML文件,逐字符读取并跟踪当前上下文状态,仅转义非合法标签位置的尖括号,内存占用稳定在KB级,不受文件大小限制。
状态机逻辑说明
跟踪3种核心状态:
- 普通文本/属性值内:遇到
</>直接转义 - 标签内部:仅识别合法标签字符,遇到异常
</>判定为需要转义 - 特殊段(CDATA、注释、处理指令):内部的
</>不需要处理
代码示例:
using System.Text; public static void RepairLargeXml(string inputPath, string outputPath) { using var reader = new StreamReader(inputPath, Encoding.UTF8); using var writer = new StreamWriter(outputPath, false, Encoding.UTF8); var state = ParseState.Normal; var tagBuffer = new StringBuilder(); while (!reader.EndOfStream) { var c = (char)reader.Read(); switch (state) { case ParseState.Normal: if (c == '<') { // 预读判断是不是合法标签开头 var next = (char)reader.Peek(); if (char.IsLetter(next) || next == '/' || next == '!' || next == '?') { // 合法标签开头,直接写 writer.Write(c); state = ParseState.InTag; tagBuffer.Clear(); } else { // 未转义的<,转义 writer.Write("<"); } } else if (c == '>') { // 普通文本里的>需要转义 writer.Write(">"); } else { writer.Write(c); } break; case ParseState.InTag: if (c == '>') { // 标签结束 writer.Write(c); state = ParseState.Normal; } else if (c == '\"' || c == '\'') { // 进入属性值 writer.Write(c); state = c == '\"' ? ParseState.InDoubleQuoteAttr : ParseState.InSingleQuoteAttr; } else if (c == '<') { // 标签内出现异常<,转义 writer.Write("<"); } else { tagBuffer.Append(c); writer.Write(c); } break; case ParseState.InDoubleQuoteAttr: if (c == '"') { writer.Write(c); state = ParseState.InTag; } else if (c == '<') { writer.Write("<"); } else if (c == '>') { writer.Write(">"); } else { writer.Write(c); } break; case ParseState.InSingleQuoteAttr: if (c == '\'') { writer.Write(c); state = ParseState.InTag; } else if (c == '<') { writer.Write("<"); } else if (c == '>') { writer.Write(">"); } else { writer.Write(c); } break; } } } enum ParseState { Normal, InTag, InDoubleQuoteAttr, InSingleQuoteAttr }
如果需要支持CDATA、注释等特殊结构,扩展对应的状态分支即可。
注意事项
- 修复前务必备份原文件,避免不可逆的结构损坏
- 你提供的示例中存在根标签前后不匹配的问题(开标签是
rootXML,关标签是tootXML),上述方案1的容错解析会自动识别并提示该类错误,可根据需求选择是否同步修复 - 若XML中存在大量CDATA段,优先使用方案1,避免自定义状态机漏处理特殊场景
内容的提问来源于stack exchange,提问作者cdub
相关产品推荐
相关产品推荐

