我有大批手动更新的XHTML文件。在更新的审查阶段,我想以编程方式检查文件的格式。 我目前正在使用 XmlReader ,但平均CPU所需的时间比我预期的要长得多。
XHTML文件的大小范围从4KB到40KB,每个文件的验证需要几秒钟。检查是必不可少的,但我希望尽可能缩短时间,因为在将文件读入下一个流程步骤时执行检查。
是否有更快的方法来执行简单的XML格式检查?也许使用外部XML库?
我可以确认使用XmlReader验证“常规”基于XML的内容是非常快速的,并且正如所建议的那样,问题似乎与每次验证文件时都读取XHTML DTD这一事实有关。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
请注意,除了DTD之外,还会下载相应的.ent文件(xhtml-lat1.ent,xhtml-symbol.ent,xhtml-special.ent)。
由于完全忽略DTD并不是XHTML的真正选择,因为格式良好与允许的HTML实体密切相关(例如,当忽略DTD时,&amp; nbsp;会立即引入验证错误)。
通过使用建议的自定义XmlResolver ,结合DTD和实体文件的本地(嵌入式)副本,解决了该问题。
一旦我清理了代码,我就会在这里发布解决方案
答案 0 :(得分:5)
我希望XmlReader
与while(reader.Read)() {}
成为最快的托管方法。当然不应该用秒来读取40KB ...你正在使用的输入法是什么?
您是否有一些外部(架构等)实体可以解决?如果是这样,您可以编写使用本地缓存模式而不是远程提取的自定义XmlResolver
(通过XmlReaderSettings
设置)...
以下几乎可以立即实现~300KB:
using(MemoryStream ms = new MemoryStream()) {
XmlWriterSettings settings = new XmlWriterSettings();
settings.CloseOutput = false;
using (XmlWriter writer = XmlWriter.Create(ms, settings))
{
writer.WriteStartElement("xml");
for (int i = 0; i < 15000; i++)
{
writer.WriteElementString("value", i.ToString());
}
writer.WriteEndElement();
}
Console.WriteLine(ms.Length + " bytes");
ms.Position = 0;
int nodes = 0;
Stopwatch watch = Stopwatch.StartNew();
using (XmlReader reader = XmlReader.Create(ms))
{
while (reader.Read()) { nodes++; }
}
watch.Stop();
Console.WriteLine("{0} nodes in {1}ms", nodes,
watch.ElapsedMilliseconds);
}
答案 1 :(得分:2)
答案 2 :(得分:1)
在我相当普通的笔记本电脑上,从头到尾用XmlReader
读取250K XML文档需要6毫秒。除了解析XML之外的其他东西是罪魁祸首。
答案 3 :(得分:1)
我知道即时发布,但我认为这可能是一个解决方案
这是相同的代码
public void GetDocumentStructure(int documentID)
{
string scmRepoPath = ConfigurationManager.AppSettings["SCMRepositoryFolder"];
string docFilePath = scmRepoPath + "\\" + documentID.ToString() + ".xml";
string docFilePath2 = scmRepoPath + "\\" + documentID.ToString() + "_clean.xml";
Tidy tidy = new Tidy();
tidy.Options.MakeClean = true;
tidy.Options.NumEntities = true;
tidy.Options.Xhtml = true;
// this option removes the DTD on the generated output of Tidy
tidy.Options.DocType = DocType.Omit;
FileStream input = new FileStream(docFilePath, FileMode.Open);
MemoryStream output = new MemoryStream();
TidyMessageCollection msgs = new TidyMessageCollection();
tidy.Parse(input, output, msgs);
output.Seek(0, SeekOrigin.Begin);
XmlReader rd = XmlReader.Create(output);
int node = 0;
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
while (rd.Read())
{
++node;
}
watch.Stop();
Console.WriteLine("Duration was : " + watch.Elapsed.ToString());
}
答案 4 :(得分:0)
正如其他人提到的,瓶颈很可能不是XmlReader。
检查在没有stringbuilder的情况下是否会碰巧执行大量字符串连接。
这可以真正打动你的表现。
答案 5 :(得分:0)
就个人而言,我很懒...所以我寻找已经解决问题的.NET库。尝试使用DataSet.ReadXML()
函数并捕获异常。它在解释XML格式错误方面做得非常出色。
答案 6 :(得分:0)
我正在使用此功能来验证字符串/片段
<Runtime.CompilerServices.Extension()>
Public Function IsValidXMLFragment(ByVal xmlFragment As String, Optional Strict As Boolean = False) As Boolean
IsValidXMLFragment = True
Dim NameTable As New Xml.NameTable
Dim XmlNamespaceManager As New Xml.XmlNamespaceManager(NameTable)
XmlNamespaceManager.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema")
XmlNamespaceManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
Dim XmlParserContext As New Xml.XmlParserContext(Nothing, XmlNamespaceManager, Nothing, Xml.XmlSpace.None)
Dim XmlReaderSettings As New Xml.XmlReaderSettings
XmlReaderSettings.ConformanceLevel = Xml.ConformanceLevel.Fragment
XmlReaderSettings.ValidationType = Xml.ValidationType.Schema
If Strict Then
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.ProcessInlineSchema)
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.ReportValidationWarnings)
Else
XmlReaderSettings.ValidationFlags = XmlSchemaValidationFlags.None
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.AllowXmlAttributes)
End If
AddHandler XmlReaderSettings.ValidationEventHandler, Sub() IsValidXMLFragment = False
AddHandler XmlReaderSettings.ValidationEventHandler, AddressOf XMLValidationCallBack
Dim XmlReader As Xml.XmlReader = Xml.XmlReader.Create(New IO.StringReader(xmlFragment), XmlReaderSettings, XmlParserContext)
While XmlReader.Read
'Read entire XML
End While
End Function
我正在使用此功能来验证文件:
Public Function IsValidXMLDocument(ByVal Path As String, Optional Strict As Boolean = False) As Boolean
IsValidXMLDocument = IO.File.Exists(Path)
If Not IsValidXMLDocument Then Exit Function
Dim XmlReaderSettings As New Xml.XmlReaderSettings
XmlReaderSettings.ConformanceLevel = Xml.ConformanceLevel.Document
XmlReaderSettings.ValidationType = Xml.ValidationType.Schema
If Strict Then
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.ProcessInlineSchema)
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.ReportValidationWarnings)
Else
XmlReaderSettings.ValidationFlags = XmlSchemaValidationFlags.None
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.AllowXmlAttributes)
End If
XmlReaderSettings.CloseInput = True
AddHandler XmlReaderSettings.ValidationEventHandler, Sub() IsValidXMLDocument = False
AddHandler XmlReaderSettings.ValidationEventHandler, AddressOf XMLValidationCallBack
Using FileStream As New IO.FileStream(Path, IO.FileMode.Open)
Using XmlReader As Xml.XmlReader = Xml.XmlReader.Create(FileStream, XmlReaderSettings)
While XmlReader.Read
'Read entire XML
End While
End Using
End Using
End Function