.NET XmlReader处理cXML时DTD验证报错,如何配置XmlReaderSettings?
在.NET 4.8、6、7中处理cXML文件时,使用XmlReader开启DTD验证会抛出以下异常:
Unhandled exception. System.Xml.Schema.XmlSchemaException: The parameter entity replacement text must nest properly within markup declarations.
示例cXML输入:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.041/cXML.dtd"> <cXML payloadID="donkeys@example.com" timestamp="2023-02-13T01:01:01Z"> <Header> </Header> <Request deploymentMode="production"> </Request> </cXML>
示例应用代码:
using System.Xml; using System.Xml.Linq; namespace Donkeys { internal class Program { static void Main() { XmlReaderSettings settings = new() { XmlResolver = new XmlUrlResolver(), DtdProcessing = DtdProcessing.Parse, ValidationType = ValidationType.DTD, }; FileStream fs = File.OpenRead("test.xml"); // 上述示例cXML文件 XmlReader reader = XmlReader.Create(fs, settings); XDocument.Load(reader); // 此处抛出异常 } } }
目前的情况是:希望通过XmlUrlResolver缓存DTD,但开启DTD验证就会触发上述错误。尝试过不同验证标志,仅当ValidationType = ValidationType.DTD时会验证并报错。XmlUrlResolver工作正常,子类化后能正确返回MemoryStream形式的DTD。
临时解决方案是通过添加验证事件处理程序忽略该错误,但这种方式不够稳妥:
using System.Xml; using System.Xml.Linq; namespace Donkeys { internal class Program { static void Main() { XmlReaderSettings settings = new() { XmlResolver = new XmlUrlResolver(), DtdProcessing = DtdProcessing.Parse, ValidationType = ValidationType.DTD, IgnoreComments = true }; settings.ValidationEventHandler += Settings_ValidationEventHandler; FileStream fs = File.OpenRead("test.xml"); XmlReader reader = XmlReader.Create(fs, settings); XDocument dogs = XDocument.Load(reader); } private static void Settings_ValidationEventHandler(object? sender, System.Xml.Schema.ValidationEventArgs e) { // 这种匹配消息的方式很脆弱 if (e.Message.Equals("The parameter entity replacement text must nest properly within markup declarations.", StringComparison.OrdinalIgnoreCase)) return; throw e.Exception; } } }
请问是否可以通过配置XmlReaderSettings的某些参数,让.NET原生处理这类cXML而不抛出异常?
这个问题的根源在于,cXML的DTD使用了一些不符合.NET XmlReader严格DTD验证规则的语法(具体是参数实体的嵌套方式不满足.NET的解析要求),而.NET的XmlReader对DTD的兼容性比部分其他XML解析器更严格。
目前没有直接通过XmlReaderSettings配置就能完全解决的方案,不过可以参考以下更稳健的处理方式:
1. 优化验证事件处理逻辑
如果必须保留DTD验证,不要直接匹配错误消息文本(容易因语言、版本更新失效),可以通过异常的类型和HResult来判断:
private static void Settings_ValidationEventHandler(object? sender, System.Xml.Schema.ValidationEventArgs e) { if (e.Exception is XmlSchemaException schemaEx && schemaEx.HResult == -2146232000) // 对应该特定DTD验证错误的HResult { return; } throw e.Exception; }
注:HResult值需要提前确认是否在你的.NET版本中稳定对应该错误。
2. 预加载并修正DTD
如果有权限或能力,可以预先下载cXML的DTD,修改其中不符合.NET验证规则的参数实体嵌套部分,然后通过自定义XmlResolver返回修改后的本地DTD。这种方式能从根源解决验证错误,但需要维护DTD的同步更新。
3. 关闭DTD验证但保留实体解析
如果业务场景中不需要严格的DTD验证,仅需要解析XML中的实体引用,可以将ValidationType设为ValidationType.None,同时保留DtdProcessing = DtdProcessing.Parse:
XmlReaderSettings settings = new() { XmlResolver = new XmlUrlResolver(), DtdProcessing = DtdProcessing.Parse, ValidationType = ValidationType.None, // 关闭验证 };
这种方式不会触发验证错误,同时仍能解析DTD中的实体,也能通过XmlUrlResolver缓存DTD。
内容的提问来源于stack exchange,提问作者user3501292

