该文档如下所示:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> .... </head> <body> ... </body> </html>
因为文档包含各种char实体(& nbsp;等等),所以我需要使用DTD,以便加载一个XmlReader.所以我的代码如下所示:
var s = File.OpenRead(fileToRead) var reader = XmlReader.Create(s,new XmlReaderSettings{ ProhibitDtd=false });
但是当我运行它时,它返回
An error has occurred while opening external DTD ‘07001’: The remote server returned an error: (503) Server Unavailable.
现在,我知道为什么我得到503错误. W3C explained it very clearly.
我看到“解决方法”,人们只是禁用DTD.这是ProhibitDtd = true可以做的,它消除了503错误.
但是在我的情况下,导致其他问题 – 应用程序没有获得实体定义,因此XML格式格式不正确.如何使用DTD进行验证,并获取实体定义,而不必打到w3.org网站?
我认为.NET 4.0有一个漂亮的内置功能来处理这种情况:XmlPreloadedResolver.但是我需要一个.NET 3.5的解决方案.
有关:
– java.io.IOException: Server returned HTTP response code: 503
解决方法
好的,所以.. XmlResolver.我创建了一个新类,派生自XmlResolver,并且覆盖了三个关键的事情:Credentials(set),ResolveUri和GetEntity.
public sealed class XhtmlResolver : XmlResolver { public override System.Net.ICredentials Credentials { set { throw new NotSupportedException();} } public override object GetEntity(Uri absoluteUri,string role,Type t) { ... } public override Uri ResolveUri(Uri baseUri,string relativeUri) { ... } }
关于这个东西的文件很简单,所以我会告诉你我学到了什么.这个类的操作是这样的:XmlReader首先调用ResolveUri,然后给定一个已解决的Uri,然后调用GetEntity.该方法预期返回类型t的对象(作为参数传递).我只看过它请求一个System.IO.Stream.
我的想法是使用csc.exe / resource选项将DTD的本地副本及其XHTML1.0的依赖项嵌入到程序集中,然后检索该资源的流.
private System.IO.Stream GetStreamForNamedResource(string resourceName) { Assembly a = Assembly.GetExecutingAssembly(); return a.GetManifestResourceStream(resourceName); }
很简单这从GetEntity()调用.
但我可以改善这一点.我们首先将它们以简明的方式嵌入DTD中,然后修改上述方法,如下所示:
private System.IO.Stream GetStreamForNamedResource(string resourceName) { Assembly a = Assembly.GetExecutingAssembly(); return new System.IO.Compression.GZipStream(a.GetManifestResourceStream(resourceName),System.IO.Compression.CompressionMode.Decompress); }
该代码打开嵌入式资源的流,并返回配置为解压缩的GZipStream.读者得到明文DTD.
我想做的只是解决Xhtml 1.0中DTD的URI.所以我写了ResolveUri和GetEntity来寻找这些特定的DTD,并且只对他们作出肯定的回应.
对于具有DTD语句的XHTML文档,流程是这样的;
> XmlReader调用ResolveUri与XHTML DTD的公共URI,它是“ – // W3C // DTD XHTML 1.0 Transitional // EN”.如果XmlResolver可以解析,它应该返回一个有效的URI.如果不能解决,应该抛出.我的实现只是抛出公共URI.
> XmlReader然后使用DTD的系统标识符调用ResolveUri,在这种情况下,它是“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”.在这种情况下,XhtmlResolver返回一个有效的Uri.
> XmlReader然后使用该URI调用GetEntity. XhtmlResolver抓住嵌入的资源流并返回.
对于依赖项 – xhtml_lat1.ent等也是一样的事情.为了使解析器工作,所有这些事情都需要嵌入.
是的,如果Resolver无法解析URI,那么预计会抛出异常.据我所知,这并没有正式记录.这似乎有点令人惊讶(严重违反the principle of least astonishment).如果相反,ResolveUri返回null,XmlReader将调用GetAntity的空URI,哪个….啊,是绝望的.
这对我有用它应该适用于从.NET处理XHTML的任何人.如果你想在你自己的应用程序中使用这个,grab the DLL.该zip包含完整的源代码.根据MS Public License许可.
您可以将其插入到XHTML中的XML应用程序中.使用它像这样:
// for an XmlDocument... System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); doc.XmlResolver = new Ionic.Xml.XhtmlResolver(); doc.Load(xhtmlFile); // for an XmlReader... var xmlReaderSettings = new XmlReaderSettings { ProhibitDtd = false,XmlResolver = new XhtmlResolver() }; using (var stream = File.OpenRead(fileToRead)) { XmlReader reader = XmlReader.Create(stream,xmlReaderSettings); while (reader.Read()) { ... }