共用方式為


HOW TO:從 XmlReader 串流 XML 片段

當您必須處理大型 XML 檔案時,可能無法將整個 XML 樹狀結構載入記憶體中。 這個主題顯示如何使用 XmlReader 串流片段。

使用 XmlReader 讀取 XElement 物件的其中一個最有效方式為,撰寫您自己自訂的座標軸方法。 座標軸方法通常會傳回集合,例如,XElementIEnumerable,如本主題的範例中所示。 在自訂座標軸方法中,呼叫 ReadFrom 方法來建立 XML 片段後,使用 yield return 傳回集合。 這會將延後執行語意 (Semantics) 提供給您自訂的座標軸方法。

當您從 XmlReader 物件建立 XML 樹狀結構時,XmlReader 必須定位在項目上。 ReadFrom 方法在讀取項目的關閉標記前不會傳回。

如果您要建立部分樹狀結構,您可以具現化 XmlReader、將讀取器定位在您要轉換為 XElement 樹狀結構的節點上,然後建立 XElement 物件。

HOW TO:具有標頭資訊存取權的資料流 XML 片段主題包含如何串流更複雜文件的資訊與範例。

HOW TO:執行大型 XML 文件的資料流轉換主題包含使用 LINQ to XML 轉換非常大的 XML 文件,同時維護小的記憶體使用量的範例。

範例

這個範例會建立自訂座標軸方法。 您可以使用 LINQ 查詢進行查詢。自訂座標軸方法 StreamRootChildDoc 是一種特別針對讀取具有重複 Child 項目的文件而設計之方法。

注意事項注意事項

下列範例使用 C# 的 yield return 建構函式。在 Visual Basic 中,對等程式碼是使用實作 IEnumerable(Of XElement) 介面的類別來提供。如需在 Visual Basic 中實作 IEnumerable(Of T) 的範例,請參閱逐步解說:在 Visual Basic 中實作 IEnumerable(Of T)

static IEnumerable<XElement> StreamRootChildDoc(StringReader stringReader)
{
    using (XmlReader reader = XmlReader.Create(stringReader))
    {
        reader.MoveToContent();
        // Parse the file and display each of the nodes.
        while (reader.Read())
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element:
                    if (reader.Name == "Child") {
                        XElement el = XElement.ReadFrom(reader) as XElement;
                        if (el != null)
                            yield return el;
                    }
                    break;
            }
        }
    }
}

static void Main(string[] args)
{
    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);
    }
}
Module Module1
    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 New 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

Public Class StreamRootChildDoc
    Implements IEnumerable(Of XElement)

    Private _stringReader As IO.StringReader

    Public Sub New(ByVal stringReader As IO.StringReader)
        _stringReader = stringReader
    End Sub

    Public Function GetEnumerator() As IEnumerator(Of XElement) Implements IEnumerable(Of XElement).GetEnumerator
        Return New StreamChildEnumerator(_stringReader)
    End Function

    Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
        Return Me.GetEnumerator()
    End Function
End Class

Public Class StreamChildEnumerator
    Implements IEnumerator(Of XElement)

    Private _current As XElement
    Private _reader As Xml.XmlReader
    Private _stringReader As IO.StringReader

    Public Sub New(ByVal stringReader As IO.StringReader)
        _stringReader = stringReader
        _reader = Xml.XmlReader.Create(_stringReader)
        _reader.MoveToContent()
    End Sub

    Public ReadOnly Property Current As XElement Implements IEnumerator(Of XElement).Current
        Get
            Return _current
        End Get
    End Property

    Public ReadOnly Property Current1 As Object Implements IEnumerator.Current
        Get
            Return Me.Current
        End Get
    End Property

    Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
        While _reader.Read()
            Select Case _reader.NodeType
                Case Xml.XmlNodeType.Element
                    Dim el = TryCast(XElement.ReadFrom(_reader), XElement)
                    If el IsNot Nothing Then
                        _current = el
                        Return True
                    End If
            End Select
        End While

        Return False
    End Function

    Public Sub Reset() Implements IEnumerator.Reset
        _reader = Xml.XmlReader.Create(_stringReader)
        _reader.MoveToContent()
    End Sub

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                _reader.Close()
            End If
        End If
        Me.disposedValue = True
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

這個範例會產生下列輸出:

bbb
ccc

在此範例中,來源文件很小。 但是,即使有數百萬的 Child 元素,此範例的記憶體使用量還是很小。

請參閱

概念

剖析 XML