6

我有大量手动更新的 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 时,  会立即引入验证错误)。


该问题通过按照建议使用自定义 XmlResolver并结合 DTD 和实体文件的本地(嵌入)副本来解决。

清理代码后,我将在此处发布解决方案

4

7 回答 7

5

我希望这XmlReaderwhile(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);
    }
于 2009-02-09T08:34:59.010 回答
2

XmlReader通过传入XmlReaderSettings具有ConformanceLevel.Document.

这将验证格式良好。

这个MSDN 文章应该解释细节。

于 2009-02-09T08:40:35.797 回答
1

在我相当普通的笔记本电脑上,从头到尾读取一个 250K 的 XML 文档XmlReader需要 6 毫秒。除了解析 XML 之外,还有其他东西是罪魁祸首。

于 2009-02-09T11:30:34.580 回答
1

我知道我在发帖,但我认为这可能是一个解决方案

  1. 使用 HTML Tidy 清除您的 xml。设置删除文档类型的选项
  2. 然后从 tidy 中读取生成的 xhtml/xml。

这是相同的代码

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());
    }
于 2011-03-11T02:35:41.117 回答
0

正如其他人所提到的,瓶颈很可能不是 XmlReader。

检查您是否不会碰巧在没有字符串生成器的情况下进行大量字符串连接。

这真的可以摧毁你的表现。

于 2009-02-09T15:52:01.690 回答
0

就个人而言,我很懒……所以我寻找已经解决问题的 .NET 库。尝试使用该DataSet.ReadXML()函数并捕获异常。它在解释 XML 格式错误方面做得非常出色。

于 2009-02-10T04:40:14.650 回答
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
于 2019-10-15T20:14:54.613 回答