我正在尝试对xhtml文档进行xpath查询。使用.NET 3.5。
该文件如下:
<!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实体(
等),所以我需要使用DTD,以便用XmlReader加载它。所以我的代码看起来像这样:
var s = File.OpenRead(fileToRead)
var reader = XmlReader.Create(s, new XmlReaderSettings{ ProhibitDtd=false });
但是当我运行它时,它会返回
打开外部DTD“http://www.w3.org/TR/xhtml1-transitional.dtd”时发生错误:远程服务器返回错误:(503)服务器不可用。
现在,我知道为什么我收到503错误。 W3C explained it very clearly。
我见过“解决方法”,人们只是禁用DTD。这是ProhibitDtd=true
可以做的,它消除了503错误。
但在我的情况下导致其他问题 - 应用程序没有得到实体定义,因此不是格式良好的XML。如何在不访问w3.org网站的情况下验证DTD并获取实体定义?
我认为.NET 4.0有一个非常好的内置功能来处理这种情况:XmlPreloadedResolver。但我需要.NET 3.5的解决方案。
:
- java.io.IOException: Server returned HTTP response code: 503
答案 0 :(得分:7)
答案是,我必须提供自己的XmlResolver。我不认为这是.NET 3.5内置的。那令人费解。令人费解的是,我花了很长时间才发现这个问题。令人费解的是,我找不到其他人已经解决了这个问题?
好的,所以...... 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
选项将XDML1.0的DTD及其依赖项的本地副本嵌入到程序集中,然后检索该资源的流。
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使用XHTML DTD的公共URI调用ResolveUri,即"-//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,等等。为了使解析器工作,所有这些都需要嵌入。
是的,如果解析器无法解析URI,则应该抛出异常。据我所知,这还没有正式记录。这似乎有点令人惊讶。 (严重违反the principle of least astonishment)。如果相反,ResolveUri返回null,XmlReader将在空URI上调用GetEntity,这......啊,是没有希望的。
这对我有用。它适用于从.NET进行XHTML XML处理的任何人。如果您想在自己的应用程序中使用它,grab the DLL。该zip包含完整的源代码。根据{{3}}许可。
您可以将其插入使用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())
{
...
}
答案 1 :(得分:3)
您可以通过将 XmlReaderSettings.XmlResolver 属性设置为null来禁止XmlReader打开任何外部资源。
System.Xml.XmlReaderSettings xmlReaderSettings = new System.Xml.XmlReaderSettings ();
xmlReaderSettings.XmlResolver = null;
System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(myUrl, xmlReaderSettings);
答案 2 :(得分:2)
当您的ResolveUri
方法获取对-//W3C//ELEMENTS XHTML Images 1.0//EN
之类的URI的“公共”形式的请求时,您的方法会抛出并等待以{{1}开头的后续类似网络的URI }?
我将公共URI解析为相应的http://
URI(然后在我的http://
方法中拦截对GetEntity
URI的请求),而不是抛出。
因此我永远不必扔掉,我认为这是正确的解决方案。
这是一种聪明的方法。你的字典有多大?我指向你的库只处理XHTML 1.0,并且只需要映射一个公共URI库。
我正在使用“模块化”的XHTML 1.1,所以我必须映射大约40个文件。
请注意框架的行为可能已经改变了!我有一个使用.NET Framework 2构建的库(包括我的XhtmlUrlResolver类),但根据应用程序(使用该库)是为.NET 2还是.NET 4构建而调用它会有所不同。
使用.NET 2,当我的ResolveUri方法总是只透明地委托给XmlUrlResolver时,它会:
使用.NET 4,每个资源都有一个额外的调用:
http://
文件),我的实现刚刚委托给XmlUrlResolver 抛出所有这些WebExceptions减慢了处理速度,这就是为什么我重新审视它以寻找修复。
你的建议,我从ResolveUri抛出,解决了这个问题,我感谢你;但是不是投掷,而是从ResolveUri返回更优雅的东西(并且更快一点:减少40个例外)。
这是我目前的源代码。
*.mod