Jak przesyłać strumieniowo fragmenty XML z elementu XmlReader (LINQ to XML)
W przypadku konieczności przetwarzania dużych plików XML może nie być możliwe załadowanie całego drzewa XML do pamięci. W tym artykule pokazano, jak przesyłać strumieniowo fragmenty przy użyciu elementów XmlReader w językach C# i Visual Basic.
Jednym z najskuteczniejszych sposobów używania obiektu XmlReader do odczytywania XElement obiektów jest napisanie własnej niestandardowej metody osi. Metoda osi zwykle zwraca kolekcję, taką jak IEnumerable<T> XElement, jak pokazano w przykładzie w tym artykule. W metodzie osi niestandardowej po utworzeniu fragmentu XML przez wywołanie ReadFrom metody zwróć kolekcję przy użyciu metody yield return
. Zapewnia to semantyka odroczonego wykonywania do niestandardowej metody osi.
Podczas tworzenia drzewa XML na podstawie XmlReader obiektu XmlReader element musi być umieszczony na elemecie . Metoda ReadFrom nie zwraca się, dopóki nie odczytuje tagu zamknięcia elementu.
Jeśli chcesz utworzyć drzewo częściowe, możesz utworzyć wystąpienie XmlReaderobiektu , umieścić czytnik w węźle XElement , który chcesz przekonwertować na drzewo, a następnie utworzyć XElement obiekt.
Artykuł Jak przesyłać strumieniowo fragmenty XML z dostępem do informacji nagłówka zawiera informacje na temat przesyłania strumieniowego bardziej złożonego dokumentu.
Artykuł How to perform streaming transform of large XML documents (Jak wykonać transformację strumieniową dużych dokumentów XML) zawiera przykład użycia linQ to XML do przekształcania bardzo dużych dokumentów XML przy zachowaniu małej ilości pamięci.
Przykład: Tworzenie niestandardowej metody osi
W tym przykładzie zostanie utworzona metoda osi niestandardowej. Zapytanie można wykonać za pomocą zapytania LINQ. Metoda StreamRootChildDoc
osi niestandardowej może odczytać dokument z powtarzającym się Child
elementem.
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
Ten przykład generuje następujące wyniki:
bbb
ccc
Technika używana w tym przykładzie utrzymuje niewielkie zużycie pamięci nawet dla milionów Child
elementów.