XmlReader から XML フラグメントをストリーム配信する方法 (LINQ to XML)
大きな XML ファイルを処理する必要があるときに、XML ツリー全体をメモリに読み込むことができない場合があります。 この記事では、C# および Visual Basic で XmlReader を使用してフラグメントをストリーム配信する方法を示します。
XmlReader を使用して XElement オブジェクトを読み取るための最も効果的な方法の 1 つは、カスタムの軸メソッドを独自に記述することです。 一般に軸メソッドは、この記事の例で示すように、XElement の IEnumerable<T> などのコレクションを返します。 カスタムの軸メソッドでは、ReadFrom メソッドを呼び出して XML フラグメントを作成した後に、yield return
を使用してコレクションを返します。 これにより、カスタムの軸メソッドに遅延実行セマンティクスが付加されます。
XmlReader オブジェクトから XML ツリーを作成する場合は、XmlReader を要素に配置する必要があります。 ReadFrom メソッドは、要素の終了タグを読み取るまで制御を戻しません。
部分ツリーを作成する場合は、XmlReader をインスタンス化し、XElement ツリーに変換するノード上にリーダーを配置し、XElement オブジェクトを作成します。
「ヘッダー情報にアクセスして XML フラグメントをストリーム出力する方法」という記事には、より複雑なドキュメントをストリーム出力する方法が記載されています。
「大きな XML ドキュメントのストリーミング変換を実行する方法」という記事には、LINQ to XML を使用し、メモリ占有領域を低く抑えながら非常に大きな XML ドキュメントを変換する例が記載されています。
例: カスタムの軸メソッドを作成する
次の例では、カスタムの軸メソッドを作成します。 このメソッドに対してクエリを実行するには、LINQ クエリを使用します。 カスタムの軸メソッド StreamRootChildDoc
では、繰り返す Child
要素が含まれるドキュメントを読み取ることができます。
using System.Xml;
using System.Xml.Linq;
static IEnumerable<XElement> StreamRootChildDoc(StringReader stringReader)
{
using XmlReader reader = XmlReader.Create(stringReader);
reader.MoveToContent();
// Parse the file and display each of the nodes.
while (true)
{
// If the current node is an element and named "Child"
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Child")
{
// Get the current node and advance the reader to the next
if (XNode.ReadFrom(reader) is XElement el)
yield return el;
}
else if (!reader.Read())
break;
}
}
string markup = """
<Root>
<Child Key="01">
<GrandChild>aaa</GrandChild>
</Child>
<Child Key="02">
<GrandChild>bbb</GrandChild>
</Child>
<Child Key="03">
<GrandChild>ccc</GrandChild>
</Child>
</Root>
""";
IEnumerable<string> grandChildData =
from el in StreamRootChildDoc(new StringReader(markup))
where (int)el.Attribute("Key") > 1
select (string)el.Element("GrandChild");
foreach (string str in grandChildData)
Console.WriteLine(str);
Imports System.Xml
Module Module1
Public Iterator Function StreamRootChildDoc(stringReader As IO.StringReader) As IEnumerable(Of XElement)
Using reader As XmlReader = XmlReader.Create(stringReader)
reader.MoveToContent()
' Parse the file and display each of the nodes.
While True
' If the current node is an element and named "Child"
If reader.NodeType = XmlNodeType.Element And reader.Name = "Child" Then
' Get the current node and advance the reader to the next
Dim el As XElement = TryCast(XNode.ReadFrom(reader), XElement)
If (el IsNot Nothing) Then
Yield el
End If
ElseIf Not reader.Read() Then
Exit While
End If
End While
End Using
End Function
Sub Main()
Dim markup = "<Root>
<Child Key=""01"">
<GrandChild>aaa</GrandChild>
</Child>
<Child Key=""02"">
<GrandChild>bbb</GrandChild>
</Child>
<Child Key=""03"">
<GrandChild>ccc</GrandChild>
</Child>
</Root>"
Dim grandChildData =
From el In StreamRootChildDoc(New IO.StringReader(markup))
Where CInt(el.@Key) > 1
Select el.<GrandChild>.Value
For Each s In grandChildData
Console.WriteLine(s)
Next
End Sub
End Module
この例を実行すると、次の出力が生成されます。
bbb
ccc
この例で使用されている手法では、Child
要素が大量にある場合でも、メモリ占有領域が少ない状態で維持されます。
関連項目
.NET