问题描述
我想解析可能有也可能没有 DTD 的 XML 文档。如果有 DTD,那么我想用它来扩展实体引用。如果没有DTD(更严格地说,如果没有DOCTYPE声明),我只想忽略DTD处理。
当我设置 XmlReaderSettings.DtdProcessing=Ignore
时,包含实体引用的文档解析失败,因为实体被视为未声明。
当我设置 XmlReaderSettings.DtdProcessing=Parse
时,解析没有 DOCTYPE 声明的文档会失败并出现一个相当模糊的诊断:
The empty string '' is not a valid name. (Parameter 'docTypeName')
System.ArgumentException: The empty string '' is not a valid name. (Parameter 'docTypeName')
at System.Xml.DtdParser.InitializefreeFloatingDtd(String baseUri,String docTypeName,String publicId,String systemId,String internalSubset,IDtdParserAdapter adapter)
at System.Xml.DtdParser.System.Xml.IDtdParser.ParseFreeFloatingDtd(String baseUri,IDtdParserAdapter adapter)
at System.Xml.XmlTextReaderImpl.ParseDtdFromParserContext()
at System.Xml.XmlTextReaderImpl.ProcessDtdFromParserContext(XmlParserContext context)
at System.Xml.XmlTextReaderImpl.FinishInitStream()
at System.Xml.XmlTextReaderImpl..ctor(Stream stream,Byte[] bytes,Int32 byteCount,XmlReaderSettings settings,Uri baseUri,String baseUriStr,XmlParserContext context,Boolean closeInput)
at System.Xml.XmlReaderSettings.CreateReader(Stream input,String baseUriString,XmlParserContext inputContext)
at System.Xml.XmlReader.Create(Stream input,XmlParserContext inputContext)
是否有任何设置可以处理这两种情况,还是我需要先查看内容并相应地设置解析器?
我怀疑这可能与 XmlParserContext
有关(考虑到堆栈跟踪中的此功能),但我不知道如何初始化 XmlParserContext
,因为我不知道提前知道文档中的内容。我提供了一个 XmlParserContext
,其中没有为 docTypeName
提供值。
解决方法
好的,我想我已经解决了。我走下了许多错误的道路,有些是我自己造成的,有些是由糟糕的文档引发的。
我现在正在创建一个这样的解析器,无论是否有 DOCTYPE 声明:
XmlReaderSettings settings = new XmlReaderSettings();
settings.ProhibitDtd = false;
settings.MaxCharactersFromEntities = 1024;
settings.XmlResolver = new MyXmlResolver(new Uri(source.getSystemId()));
xmlReader = XmlReader.Create(inputStream,settings);
不需要 XmlParserContext
对象,它似乎只是碍手碍脚(而且我仍然不知道它到底是做什么的)。
我没有找到同时为解析器提供输入流和基 URI 的方法,因此我使用自己的 XmlResolver 知道基 URI,并使用它代替提供给 XmlResolver 的 { {1}} 方法。
我观察到的失败原因如下:
(a) ResolveUri
是由我尝试提供 System.ArgumentException
引起的,当我切换到一个没有使用它的构造函数时就消失了。 (谢谢,@jdweng)。
(b) “实体未声明”错误是由解析 DTD 系统 ID 的静默失败引起的,而这又是由于我没有找到任何成功的方法来为解析器提供基本 URI(我尝试使用 XmlParserContext
执行此操作已被证明是错误的)。