2012-12-22 37 views
15

XDocument.Loadを使用して新しいXDocumentを作成すると、XMLファイルが開き、ローカルコピーが保持されますか、またはハードドライブからドキュメントが継続的に読み取られますか。それが継続的に読み取られる場合、XMLを解析するより速い方法がありますか?XDocumentのパフォーマンス

XDocument x = XDocument.Load("file.xml"); 

答えて

10

考慮すべき測定のカップルがあります。

  1. リニアトラバース速度(例えば読み/ロード)
  2. オンデマンドクエリの速度

即座に答えるためには、質問:XDocumentは、XmlReaderを使用して、各要素を読み取って文書をメモリに読み込み、対応するXElementインスタンスを作成します(下記のコードを参照)。そのため、かなり速く(ほとんどの目的には十分に速い)、大きなドキュメントを解析するときに大量のメモリを消費する可能性があります。

未加工のXmlReaderは、ドキュメントをメモリに保持せずに行うことができるものに限定されている場合、トラバーサルに最適です。他のノード(例えば、親ノードと子ノードのリンク)に関して重要な構造が作成されず、解決されないので、他の方法より優れている。ただし、オンデマンドクエリ機能はほとんど存在しません。各ノードにある値に反応することができますが、文書全体を照会することはできません。文書をもう一度見る必要がある場合は、もう一度文書全体を走査しなければなりません。これに対して、XDocumentは新しいオブジェクトをインスタンス化し、基本的な構造作業を実行するため、トラバースに時間がかかります。また、ソースのサイズに比例したメモリを消費します。これらのトレードオフと引き換えに、優れたクエリ能力が得られます。

mentioned by Jon Skeetのようなアプローチを組み合わせることができ、ここに表示されます。Streaming Into LINQ to XML Using C# Custom Iterators and XmlReader

XDocumentロードのソース()

public static XDocument Load(Stream stream, LoadOptions options) 
{ 
    XmlReaderSettings xmlReaderSettings = XNode.GetXmlReaderSettings(options); 
    XDocument result; 
    using (XmlReader xmlReader = XmlReader.Create(stream, xmlReaderSettings)) 
    { 
     result = XDocument.Load(xmlReader, options); 
    } 
    return result; 
} 

// which calls... 

public static XDocument Load(XmlReader reader, LoadOptions options) 
{ 
    if (reader == null) 
    { 
     throw new ArgumentNullException("reader"); 
    } 
    if (reader.ReadState == ReadState.Initial) 
    { 
     reader.Read(); 
    } 
    XDocument xDocument = new XDocument(); 
    if ((options & LoadOptions.SetBaseUri) != LoadOptions.None) 
    { 
     string baseURI = reader.BaseURI; 
     if (baseURI != null && baseURI.Length != 0) 
     { 
      xDocument.SetBaseUri(baseURI); 
     } 
    } 
    if ((options & LoadOptions.SetLineInfo) != LoadOptions.None) 
    { 
     IXmlLineInfo xmlLineInfo = reader as IXmlLineInfo; 
     if (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
     { 
      xDocument.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
     } 
    } 
    if (reader.NodeType == XmlNodeType.XmlDeclaration) 
    { 
     xDocument.Declaration = new XDeclaration(reader); 
    } 
    xDocument.ReadContentFrom(reader, options); 
    if (!reader.EOF) 
    { 
     throw new InvalidOperationException(Res.GetString("InvalidOperation_ExpectedEndOfFile")); 
    } 
    if (xDocument.Root == null) 
    { 
     throw new InvalidOperationException(Res.GetString("InvalidOperation_MissingRoot")); 
    } 
    return xDocument; 
} 

// which calls... 

internal void ReadContentFrom(XmlReader r, LoadOptions o) 
{ 
    if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == LoadOptions.None) 
    { 
     this.ReadContentFrom(r); 
     return; 
    } 
    if (r.ReadState != ReadState.Interactive) 
    { 
     throw new InvalidOperationException(Res.GetString("InvalidOperation_ExpectedInteractive")); 
    } 
    XContainer xContainer = this; 
    XNode xNode = null; 
    NamespaceCache namespaceCache = default(NamespaceCache); 
    NamespaceCache namespaceCache2 = default(NamespaceCache); 
    string text = ((o & LoadOptions.SetBaseUri) != LoadOptions.None) ? r.BaseURI : null; 
    IXmlLineInfo xmlLineInfo = ((o & LoadOptions.SetLineInfo) != LoadOptions.None) ? (r as IXmlLineInfo) : null; 
    while (true) 
    { 
     string baseURI = r.BaseURI; 
     switch (r.NodeType) 
     { 
     case XmlNodeType.Element: 
     { 
      XElement xElement = new XElement(namespaceCache.Get(r.NamespaceURI).GetName(r.LocalName)); 
      if (text != null && text != baseURI) 
      { 
       xElement.SetBaseUri(baseURI); 
      } 
      if (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
      { 
       xElement.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
      } 
      if (r.MoveToFirstAttribute()) 
      { 
       do 
       { 
        XAttribute xAttribute = new XAttribute(namespaceCache2.Get((r.Prefix.Length == 0) ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value); 
        if (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
        { 
         xAttribute.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
        } 
        xElement.AppendAttributeSkipNotify(xAttribute); 
       } 
       while (r.MoveToNextAttribute()); 
       r.MoveToElement(); 
      } 
      xContainer.AddNodeSkipNotify(xElement); 
      if (r.IsEmptyElement) 
      { 
       goto IL_30A; 
      } 
      xContainer = xElement; 
      if (text != null) 
      { 
       text = baseURI; 
       goto IL_30A; 
      } 
      goto IL_30A; 
     } 
     case XmlNodeType.Text: 
     case XmlNodeType.Whitespace: 
     case XmlNodeType.SignificantWhitespace: 
      if ((text != null && text != baseURI) || (xmlLineInfo != null && xmlLineInfo.HasLineInfo())) 
      { 
       xNode = new XText(r.Value); 
       goto IL_30A; 
      } 
      xContainer.AddStringSkipNotify(r.Value); 
      goto IL_30A; 
     case XmlNodeType.CDATA: 
      xNode = new XCData(r.Value); 
      goto IL_30A; 
     case XmlNodeType.EntityReference: 
      if (!r.CanResolveEntity) 
      { 
       goto Block_25; 
      } 
      r.ResolveEntity(); 
      goto IL_30A; 
     case XmlNodeType.ProcessingInstruction: 
      xNode = new XProcessingInstruction(r.Name, r.Value); 
      goto IL_30A; 
     case XmlNodeType.Comment: 
      xNode = new XComment(r.Value); 
      goto IL_30A; 
     case XmlNodeType.DocumentType: 
      xNode = new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value, r.DtdInfo); 
      goto IL_30A; 
     case XmlNodeType.EndElement: 
     { 
      if (xContainer.content == null) 
      { 
       xContainer.content = string.Empty; 
      } 
      XElement xElement2 = xContainer as XElement; 
      if (xElement2 != null && xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
      { 
       xElement2.SetEndElementLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
      } 
      if (xContainer == this) 
      { 
       return; 
      } 
      if (text != null && xContainer.HasBaseUri) 
      { 
       text = xContainer.parent.BaseUri; 
      } 
      xContainer = xContainer.parent; 
      goto IL_30A; 
     } 
     case XmlNodeType.EndEntity: 
      goto IL_30A; 
     } 
     break; 
     IL_30A: 
     if (xNode != null) 
     { 
      if (text != null && text != baseURI) 
      { 
       xNode.SetBaseUri(baseURI); 
      } 
      if (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
      { 
       xNode.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
      } 
      xContainer.AddNodeSkipNotify(xNode); 
      xNode = null; 
     } 
     if (!r.Read()) 
     { 
      return; 
     } 
    } 
    goto IL_2E1; 
    Block_25: 
    throw new InvalidOperationException(Res.GetString("InvalidOperation_UnresolvedEntityReference")); 
    IL_2E1: 
    throw new InvalidOperationException(Res.GetString("InvalidOperation_UnexpectedNodeType", new object[] 
    { 
     r.NodeType 
    })); 
} 
1

私はそれが連続して読み込むと思わないでください。 XDocument.Loadメソッドの素晴らしい点は、XmlReaderを使用してXMLをXMLツリーに読み込むことです。これからは、ツリーのようにメモリに格納されている可能性の高いツリーを作成したので、それはもはやドキュメントを絶えず読みません。これはツリーを操作します。ツリーなので、すべての読み取りと変更がより高速に行われます。 IDisposableは実装されていませんが、自動的に破棄されます。

6

Load()を呼び出して、ドキュメントのローカルインスタンスをメモリに保持すると、受信したストリーム(ファイルまたは文字列からのものであるかどうかは問わない)を解析します。ソースは何でもかまいません(NetworkStream、DataReader、ユーザーが入力した文字列など)可能性があるので、元に戻ったり、データの状態を知らないのでデータを再度読み込めませんでした。 )。

スピードが本当に欲しいのなら、XDocumentは最初にドキュメントを解析してメモリに保持する必要があるので、作業が簡単ですが、すべてファットセットではありません。 System.Xml.XmlReaderのアプローチを使用して実際に大きな文書を扱っている場合は、ドキュメントをストリームとして読み取ることができ、現在の要素以外のものを保持する必要がないため、通常は高速です。このbenchmarkは、これに関するいくつかの興味深い図を示しています。

+0

ありがとう、これは本当に役に立ちます。私はXNAでそれを使用していました。私はXDocument.Loadと呼ぶ時間を減らす必要があると思います。なぜなら、すべてのオブジェクトでそれを行うので、私は必要ないからです。 – redcodefinal

+1

ベンチマークリンクが無効です。 – nawfal

+0

同じ記事への新しいリンクで更新されました。 –