如何從 XmlReader 串流 XML 片段 (LINQ to XML)
當您必須處理大型 XML 檔案時,可能無法將整個 XML 樹狀載入記憶體中。 本文說明如何使用 C# 和 Visual Basic 中的 XmlReader 串流片段。
使用 XmlReader 讀取 XElement 物件的其中一個最有效方式為,撰寫您自己自訂的座標軸方法。 座標軸方法通常會傳回集合,例如,IEnumerable<T> 的 XElement,如本文中的範例所示。 在自訂座標軸方法中,呼叫 ReadFrom 方法來建立 XML 片段後,使用 yield return
傳回集合。 這會將延後執行語意 (Semantics) 提供給您自訂的座標軸方法。
當您從 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
元素,此範例中使用的技術即使仍維持少量的記憶體使用量。