Che*_*eso 10 .net xml xhtml w3c dtd
我正在尝试对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>
Run Code Online (Sandbox Code Playgroud)
因为文档包含各种char实体( 等等),所以我需要使用DTD,以便使用XmlReader加载它.所以我的代码看起来像这样:
var s = File.OpenRead(fileToRead)
var reader = XmlReader.Create(s, new XmlReaderSettings{ ProhibitDtd=false });
Run Code Online (Sandbox Code Playgroud)
但是当我运行它时,它会返回
打开外部DTD'http: //www.w3.org/TR/xhtml1-transitional.dtd '时发生错误:远程服务器返回错误:(503)服务器不可用.
现在,我知道为什么我得到503错误. W3C非常清楚地解释了这一点.
我见过"解决方法",人们只是禁用DTD.这是ProhibitDtd=true可以做的,它消除了503错误.
但在我的情况下导致其他问题 - 应用程序没有获得实体定义,因此不是格式良好的XML.如何在不访问w3.org网站的情况下验证DTD并获取实体定义?
我认为.NET 4.0有一个漂亮的内置功能来处理这种情况:XmlPreloadedResolver.但我需要.NET 3.5的解决方案.
答案是,我必须提供自己的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)
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
关于这些东西的文档非常吝啬,所以我会告诉你我学到了什么.这个类的操作是这样的: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);
}
Run Code Online (Sandbox Code Playgroud)
很简单.这是从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);
}
Run Code Online (Sandbox Code Playgroud)
该代码打开嵌入式资源的流,并返回为解压缩配置的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,则应该抛出异常.据我所知,这还没有正式记录.这似乎有点令人惊讶.(严重违反最不惊讶的原则).如果相反,ResolveUri返回null,XmlReader将在空URI上调用GetEntity,这......啊,是没有希望的.
这适合我.它适用于从.NET对XHTML进行XML处理的任何人.如果要在自己的应用程序中使用它,请获取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())
{
...
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5869 次 |
| 最近记录: |