Üst bilgi bilgilerine erişimle XML parçalarını akışla aktarma (LINQ to XML)

Bazen rastgele büyük XML dosyalarını okumanız ve uygulamanızı yazmanız gerekir; böylece uygulamanın bellek ayak izi tahmin edilebilir olur. Bir XML ağacını büyük bir XML dosyasıyla doldurmaya çalışırsanız, bellek kullanımınız dosyanın boyutuyla orantılı olur; yani aşırı olur. Bu nedenle, bunun yerine bir akış tekniği kullanmalısınız.

Bir seçenek, kullanarak uygulamanızı XmlReaderyazmaktır. Ancak, XML ağacını sorgulamak için LINQ kullanmak isteyebilirsiniz. Bu durumda kendi özel eksen yönteminizi yazabilirsiniz. Daha fazla bilgi için bkz . LINQ to XML eksen yöntemi yazma.

Kendi eksen yönteminizi yazmak için, ilgilendiğiniz düğümlerden birine ulaşana kadar düğümleri okumak için kullanan XmlReader küçük bir yöntem yazarsınız. Yöntemi daha sonra dosyasından okur ve bir XML parçasının örneğini XmlReader oluşturan öğesini çağırırReadFrom. Daha sonra her bir parçayı yield return , özel eksen yönteminizi numaralandıran yönteme verir. Ardından özel eksen yönteminize LINQ sorguları yazabilirsiniz.

Akış teknikleri en iyi şekilde, kaynak belgeyi yalnızca bir kez işlemeniz gereken durumlarda uygulanır ve öğeleri belge sırasına göre işleyebilirsiniz. gibi OrderBybazı standart sorgu işleçleri kaynaklarını yineler, tüm verileri toplar, sıralar ve son olarak dizideki ilk öğeyi verir. İlk öğeyi vermeden önce kaynağını oluşturan bir sorgu işleci kullanırsanız, küçük bir bellek ayak izi tutmazsınız.

Örnek: URI tarafından belirtilen dosyadan XML parçalarını akışla aktaran özel bir eksen yöntemi uygulama ve kullanma

Bazen sorun biraz daha ilginç hale gelir. Aşağıdaki XML belgesinde, özel eksen yönteminizin tüketicisinin de her öğenin ait olduğu müşterinin adını bilmesi gerekir.

<?xml version="1.0" encoding="utf-8" ?>
<Root>
  <Customer>
    <Name>A. Datum Corporation</Name>
    <Item>
      <Key>0001</Key>
    </Item>
    <Item>
      <Key>0002</Key>
    </Item>
    <Item>
      <Key>0003</Key>
    </Item>
    <Item>
      <Key>0004</Key>
    </Item>
  </Customer>
  <Customer>
    <Name>Fabrikam, Inc.</Name>
    <Item>
      <Key>0005</Key>
    </Item>
    <Item>
      <Key>0006</Key>
    </Item>
    <Item>
      <Key>0007</Key>
    </Item>
    <Item>
      <Key>0008</Key>
    </Item>
  </Customer>
  <Customer>
    <Name>Southridge Video</Name>
    <Item>
      <Key>0009</Key>
    </Item>
    <Item>
      <Key>0010</Key>
    </Item>
  </Customer>
</Root>

Bu örnekteki yaklaşım ayrıca bu üst bilgi bilgilerini izlemek, üst bilgi bilgilerini kaydetmek ve ardından hem üst bilgi bilgilerini hem de numaralandırdığınız ayrıntıları içeren küçük bir XML ağacı oluşturmaktır. Eksen yöntemi daha sonra bu yeni, küçük XML ağacını verir. Ardından sorgunun hem üst bilgi bilgilerine hem de ayrıntı bilgilerine erişimi olur.

Bu yaklaşımın bellek ayak izi azdır. Her ayrıntı XML parçası getirildiğinden, önceki parçaya hiçbir başvuru tutulmaz ve çöp toplama için kullanılabilir. Bu teknik yığında çok sayıda kısa ömürlü nesne oluşturur.

Aşağıdaki örnekte, URI tarafından belirtilen dosyadan XML parçalarını akışla aktaran özel bir eksen yönteminin nasıl uygulanıp kullanılacağı gösterilmektedir. Bu özel eksen, , Nameve öğeleri olan Customerbir belgeyi bekleyecek ve Item bu öğelerin yukarıdaki Source.xml belgede olduğu gibi düzenlenecek şekilde yazılır. Basit bir uygulamadır. Daha sağlam bir uygulama, geçersiz bir belgeyi ayrıştırmaya hazır olacaktır.

static IEnumerable<XElement> StreamCustomerItem(string uri)
{
    using (XmlReader reader = XmlReader.Create(uri))
    {
        XElement name = null;
        XElement item = null;

        reader.MoveToContent();

        // Parse the file, save header information when encountered, and yield the
        // Item XElement objects as they're created.

        // loop through Customer elements
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element
                && reader.Name == "Customer")
            {
                // move to Name element
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element &&
                        reader.Name == "Name")
                    {
                        name = XElement.ReadFrom(reader) as XElement;
                        break;
                    }
                }

                // Loop through Item elements
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;
                    if (reader.NodeType == XmlNodeType.Element
                        && reader.Name == "Item")
                    {
                        item = XElement.ReadFrom(reader) as XElement;
                        if (item != null) {
                            XElement tempRoot = new XElement("Root",
                                new XElement(name)
                            );
                            tempRoot.Add(item);
                            yield return item;
                        }
                    }
                }
            }
        }
    }
}

static void Main(string[] args)
{
    XElement xmlTree = new XElement("Root",
        from el in StreamCustomerItem("Source.xml")
        where (int)el.Element("Key") >= 3 && (int)el.Element("Key") <= 7
        select new XElement("Item",
            new XElement("Customer", (string)el.Parent.Element("Name")),
            new XElement(el.Element("Key"))
        )
    );
    Console.WriteLine(xmlTree);
}
Module Module1

    Sub Main()
        Dim xmlTree = <Root>
                          <%=
                              From el In New StreamCustomerItem("Source.xml")
                              Let itemKey = CInt(el.<Key>.Value)
                              Where itemKey >= 3 AndAlso itemKey <= 7
                              Select <Item>
                                         <Customer><%= el.Parent.<Name>.Value %></Customer>
                                         <%= el.<Key> %>
                                     </Item>
                          %>
                      </Root>

        Console.WriteLine(xmlTree)
    End Sub

End Module

Public Class StreamCustomerItem
    Implements IEnumerable(Of XElement)

    Private _uri As String

    Public Sub New(ByVal uri As String)
        _uri = uri
    End Sub

    Public Function GetEnumerator() As IEnumerator(Of XElement) Implements IEnumerable(Of XElement).GetEnumerator
        Return New StreamCustomerItemEnumerator(_uri)
    End Function

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

Public Class StreamCustomerItemEnumerator
    Implements IEnumerator(Of XElement)

    Private _current As XElement
    Private _customerName As String
    Private _reader As Xml.XmlReader
    Private _uri As String

    Public Sub New(ByVal uri As String)
        _uri = uri
        _reader = Xml.XmlReader.Create(_uri)
        _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
        Dim item As XElement
        Dim name As XElement

        ' Parse the file, save header information when encountered, and return the
        ' current Item XElement.

        ' loop through Customer elements
        While _reader.Read()
            If _reader.NodeType = Xml.XmlNodeType.Element Then
                Select Case _reader.Name
                    Case "Customer"
                        ' move to Name element
                        While _reader.Read()

                            If _reader.NodeType = Xml.XmlNodeType.Element AndAlso
                                _reader.Name = "Name" Then

                                name = TryCast(XElement.ReadFrom(_reader), XElement)
                                _customerName = If(name IsNot Nothing, name.Value, "")
                                Exit While
                            End If

                        End While
                    Case "Item"
                        item = TryCast(XElement.ReadFrom(_reader), XElement)
                        Dim tempRoot = <Root>
                                           <Name><%= _customerName %></Name>
                                           <%= item %>
                                       </Root>
                        _current = item
                        Return True
                End Select
            End If
        End While

        Return False
    End Function

    Public Sub Reset() Implements IEnumerator.Reset
        _reader = Xml.XmlReader.Create(_uri)
        _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

Bu kod şu çıkışı oluşturur:

<Root>
  <Item>
    <Customer>A. Datum Corporation</Customer>
    <Key>0003</Key>
  </Item>
  <Item>
    <Customer>A. Datum Corporation</Customer>
    <Key>0004</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0005</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0006</Key>
  </Item>
  <Item>
    <Customer>Fabrikam, Inc.</Customer>
    <Key>0007</Key>
  </Item>
</Root>