如果有如下的Xhtml文字,在.Net中用XmlDocument.LoadXml载入的时候,速度很慢。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<p>Hello.</p>
</body>
</html>
这个xml文本和一般xml文本的差别是多了一个Doctype说明,指定了一个DTD文件。根据.Net文档中关于XmlDocument.LoadXml的说明,是不会进行DTD或者Schema的验证的,所以并不是验证消耗了时间。对于Load方法也是这样。
不过,这个xml文本比较涉及到了一个外部文本,http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd,所以微软在Load的时候,会首先载入这个文件。也就是说,在载入每一个Xhtml文件的时候,都会到www.w3.org去下载一个文件。
XmlDocument是通过XmlResolver这个属性来设置如何处理外部资源的。简单的把这个属性设置为null(Nothing),就可以彻底不解析任何外部资源。速度也就快了。
不过,这种解决方法不是彻底的解决方法,因为这样,所有的实体(Entities)都不能正确的解释,比如©之类。如果确认输入文本没有Entities,那可以这样简单的解决。
完美的解决方法是,吧Xhtml所要使用到的DTD相关文件都保存到本地目录上,让XmlDocument从本地读取Entities定义信息。和Xhtml相关的DTD文件一共有6个,在Xhtml 1.0标准附录:DTDs中已经列出,列出文件Url如下。
以上文件需要保存到本地目录中,然后用下面代码建立XhtmlUrlResolver类对Xhtml相关的DTD文件进行重新定向,定向到本地目录。
Public Class XhtmlUrlResolver
Inherits XmlUrlResolver
Private Shared theDTDCache As Dict(Of Uri)
Public Overrides Function ResolveUri(ByVal baseUri As Uri,
ByVal relativeUri As String) As Uri
Dim uri As Uri = DTDCache.Item(relativeUri)
If uri IsNot Nothing Then
Return uri
Else
Return MyBase.ResolveUri(baseUri, relativeUri)
End If
End Function
Private Shared ReadOnly Property DTDCache() As Dict(Of Uri)
Get
If theDTDCache Is Nothing Then
theDTDCache = New Dict(Of Uri)
theDTDCache.OptionCaseInsensitive = True
theDTDCache.OptionDontAddExisted = True
BuildDTDCache(theDTDCache)
End If
Return theDTDCache
End Get
End Property
Private Shared Sub BuildDTDCache(ByVal dc As Dict(Of Uri))
dc.Add("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", _
New Uri(Path.Combine(AppConfigPath, "xhtml1-strict.dtd")))
dc.Add("http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd", _
New Uri(Path.Combine(AppConfigPath, "xhtml1-transitional.dtd")))
dc.Add("http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd", _
New Uri(Path.Combine(AppConfigPath, "xhtml1-frameset.dtd")))
dc.Add("http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent", _
New Uri(Path.Combine(AppConfigPath, "xhtml1-lat1.ent")))
dc.Add("http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent", _
New Uri(Path.Combine(AppConfigPath, "xhtml1-special.ent")))
dc.Add("http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent", _
New Uri(Path.Combine(AppConfigPath, "xhtml1-symbol.ent")))
End Sub
End Class
使用上述XhtmlUrlResolver的示例代码如下。
Dim xhtml As String = _
"<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN""" & vbCrLf & _
" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">" & vbCrLf & _
"<html xmlns=""http://www.w3.org/1999/xhtml"">" & vbCrLf & _
"<head><title>Test</title></head>" & vbCrLf & _
"<body>Hello, World! © Copyright.</body>" & vbCrLf & _
"</html>"
Dim xdoc As XmlDocument = New XmlDocument
xdoc.XmlResolver = New XhtmlUrlResolver
xdoc.LoadXml(xhtml)
微软在一篇名为Creating a Custom Resolver的文章中,给出了下面这个示例。这个示例在第一次读取到远程DTD的Url的时候,会在本地目录进行保存,以后再次遇到这个DTD的时候,就从本地进行读取。这个代码挺有参考意义的。
不过需要指出的是,这篇文章已经不是在MSDN的正文里面的了,在MSDN中一个叫做“以往版本”(Previous Version)的目录下面,因此多半是一篇过气的文章,所以这个代码也只有一些参考意义。
Class CustomResolver
Inherits XmlUrlResolver
Public Shared myHash As New Hashtable()
Public Overrides Function GetEntity(ByVal absoluteUri As Uri, _
ByVal role As String, ByVal ofObjectToReturn As Type) As Object
If myHash.ContainsKey(absoluteUri) Then
Return New FileStream(CType(myHash(absoluteUri), String), _
FileMode.Open, FileAccess.Read, FileShare.Read)
Else
Dim filename As String = "c:\resolver_cache\resolve" + CStr(myHash.Count) + ".txt"
Dim fStream As New FileStream(filename, FileMode.Create)
Dim sRead As New StreamReader(CType(MyBase.GetEntity( _
absoluteUri, role, ofObjectToReturn), Stream))
Dim sWrite As New StreamWriter(fStream)
sWrite.Write(sRead.ReadToEnd())
myHash.Add(absoluteUri, filename)
sWrite.Close()
sRead.Close()
Return New FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)
End If
End Function
End Class
波波坡原创文章 链接:http://www.bobopo.com/article/code/xmldocument_xhtml.htm
标签: dotNet
关键词: LoadXml, XmlDocument, Xhtml, 速度慢, DTD, 外部资源, XmlResolver, .Net Framework, DotNet, 代码
创建日期: 2008-01-12