Megosztás a következőn keresztül:


XML-töredékek streamelése fejlécadatokhoz való hozzáféréssel (LINQ–XML)

Előfordul, hogy tetszőlegesen nagy XML-fájlokat kell beolvasnia, és meg kell írnia az alkalmazást, hogy az alkalmazás memóriaigénye kiszámítható legyen. Ha egy XML-fát nagy XML-fájllal próbál feltölteni, a memóriahasználat arányos lesz a fájl méretével, vagyis túlzottan. Ezért inkább streamelési technikát kell használnia.

Az egyik lehetőség az alkalmazás írása a következővel XmlReader: . Előfordulhat azonban, hogy a LINQ használatával szeretné lekérdezni az XML-fát. Ha igen, megírhatja saját egyéni tengelymetódusát. További információ: LINQ írása XML-tengelyre.

Saját tengelymetódus írásához írjon egy kis metódust, amely a XmlReader csomópontok olvasására használja, amíg el nem éri az egyik csomópontot, amelyben érdekli. A metódus ezután meghívja a metódust ReadFrom, amely az XmlReader XML-töredékből olvas be és példányosít. Ezután az egyes töredékeket yield return az egyéni tengelymetódus számbavételét tartalmazó metódusba adja át. Ezután linq-lekérdezéseket írhat az egyéni tengely metódusára.

A streamelési technikák akkor alkalmazhatók a legjobban, ha a forrásdokumentumot csak egyszer kell feldolgoznia, és az elemeket dokumentumsorrendben is feldolgozhatja. Bizonyos szabványos lekérdezési operátorok, például OrderBya forrás iterálása, az összes adat összegyűjtése, rendezése, majd végül a sorozat első eleme. Ha olyan lekérdezési operátort használ, amely az első elem létrehozása előtt hasznosítja a forrását, nem fog kis memóriaigényt megőrizni.

Példa: Egyéni tengelymetódus implementálása és használata, amely xml-töredékeket streamel egy URI által megadott fájlból

Néha a probléma csak egy kicsit érdekesebb lesz. Az alábbi XML-dokumentumban az egyéni tengelymetódus fogyasztójának ismernie kell annak az ügyfélnek a nevét is, amelyhez az egyes elemek tartoznak.

<?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>

A példa módszere az, hogy a fejlécinformációkat is figyeli, menti a fejlécadatokat, majd létrehoz egy kis XML-fát, amely tartalmazza a fejlécadatokat és az enumerálandó részleteket is. A tengelymetódus ezután ezt az új, kis XML-fát adja meg. A lekérdezés ezután hozzáfér a fejlécadatokhoz és a részletes információkhoz.

Ez a megközelítés kis memóriaigényű. Mivel minden részlet XML-töredékét kihozták, a rendszer nem tartalmaz hivatkozásokat az előző töredékre, és a szemétgyűjtéshez is elérhető. Ez a technika sok rövid élettartamú objektumot hoz létre a halomon.

Az alábbi példa bemutatja, hogyan implementálhat és használhat olyan egyéni tengelymetódus-metódust, amely xml-töredékeket streamel az URI által megadott fájlból. Ez az egyéni tengely úgy van megírva, hogy olyan dokumentumot vár, amely tartalmazza Customer, Nameés Item elemeit, és hogy ezek az elemek a fenti Source.xml dokumentumhoz hasonlóan lesznek elrendezve. Ez egy egyszerű megvalósítás. Egy robusztusabb implementáció készen áll egy érvénytelen dokumentum elemzésére.

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

    reader.MoveToContent();

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

    // Loop through Customer elements
    do
    {
        if (reader.NodeType == XmlNodeType.Element && reader.Name == "Customer")
        {
            // Move to Name element
            XElement? name = null;
            do
            {
                if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
                {
                    name = XNode.ReadFrom(reader) as XElement;
                    break;
                }
            }
            while (reader.Read());

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

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);
}
Imports System.Xml

Module Module1

    Public Iterator Function StreamCustomerItem(uri As String) As IEnumerable(Of XElement)
        Using reader As XmlReader = XmlReader.Create(uri)
            reader.MoveToContent()

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

            ' Loop through Customer elements
            Do

                If reader.NodeType = XmlNodeType.Element And reader.Name = "Customer" Then

                    ' Move to Name element
                    Dim name As XElement = Nothing
                    Do
                        If reader.NodeType = XmlNodeType.Element And reader.Name = "Name" Then

                            name = TryCast(XNode.ReadFrom(reader), XElement)
                            Exit Do

                        End If

                    Loop While reader.Read()

                    ' Loop through Item elements
                    While reader.NodeType <> XmlNodeType.EndElement

                        If reader.NodeType = XmlNodeType.Element And reader.Name = "Item" Then

                            Dim item = TryCast(XNode.ReadFrom(reader), XElement)

                            If name IsNot Nothing AndAlso item IsNot Nothing Then

                                Dim tempRoot = <Root>
                                                   <Name><%= name.Value %></Name>
                                                   <%= item %>
                                               </Root>

                                Yield item

                            End If

                        ElseIf Not reader.Read() Then
                            Exit While
                        End If

                    End While

                End If

            Loop While reader.Read()

        End Using
    End Function

    Sub Main()
        Dim xmlTree = <Root>
                          <%=
                              From el In 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

Ez a kód a következő kimenetet állítja elő:

<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>