在C#中以编程方式检查XML文件格式良好的最快方法是什么?

时间:2009-02-09 08:29:11

标签: c# xml well-formed

我有大批手动更新的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和实体文件的本地(嵌入式)副本,解决了该问题。

一旦我清理了代码,我就会在这里发布解决方案

7 个答案:

答案 0 :(得分: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);
    }

答案 1 :(得分:2)

通过传入XmlReader的{​​{1}}对象来创建XmlReaderSettings对象。

这将验证结构良好。

MSDN article应解释详情。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

我知道即时发布,但我认为这可能是一个解决方案

  1. 使用HTML Tidy清除xml。设置删除doctype的选项
  2. 然后从tidy读取生成的xhtml / xml。
  3. 这是相同的代码

    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