Vorgehensweise: Durchführen einer Streamingtransformation großer XML-Dokumente (C#)How to: Perform Streaming Transform of Large XML Documents (C#)

Es kann vorkommen, dass Sie große XML-Dateien transformieren und Ihre Anwendung so schreiben müssen, dass der Arbeitsspeicherbedarf der Anwendung vorhersehbar ist.Sometimes you have to transform large XML files, and write your application so that the memory footprint of the application is predictable. Wenn Sie versuchen, eine XML-Struktur mit einer sehr großen XML-Datei zu füllen, ändert sich Ihre Speicherbeanspruchung proportional zur Größe der Datei, also exzessiv.If you try to populate an XML tree with a very large XML file, your memory usage will be proportional to the size of the file (that is, excessive). Deshalb sollten Sie stattdessen ein Streamingverfahren verwenden.Therefore, you should use a streaming technique instead.

Streamingverfahren eignen sich vor allem für Situationen, bei denen Sie das Quelldokument nur einmal verarbeiten müssen und bei denen die Elemente in der Reihenfolge verarbeitet werden können, in der sie im Dokument auftreten.Streaming techniques are best applied in situations where you need to process the source document only once, and you can process the elements in document order. Einige Standardabfrageoperatoren, wie OrderBy, durchlaufen ihre Quelle, erfassen alle Daten, sortieren sie und geben dann das erste Element in der Sequenz zurück.Certain standard query operators, such as OrderBy, iterate their source, collect all of the data, sort it, and then finally yield the first item in the sequence. Beachten Sie, dass Sie bei der Verwendung eines Abfrageoperators, der seine Quelle vor der Rückgabe des ersten Elements materialisiert, keine minimale Speicherbeanspruchung für Ihre Anwendung aufrechterhalten können.Note that if you use a query operator that materializes its source before yielding the first item, you will not retain a small memory footprint for your application.

Aber selbst dann, wenn Sie das in Vorgehensweise: Streamen von XML-Fragmenten mit Zugriff auf Headerinformationen (C#) beschriebene Verfahren verwenden, wird die Speicherbeanspruchung zu groß, wenn Sie versuchen, eine XML-Struktur unter Einbeziehung des transformierten Dokuments zusammenzustellen.Even if you use the technique described in How to: Stream XML Fragments with Access to Header Information (C#), if you try to assemble an XML tree that contains the transformed document, memory usage will be too great.

Es gibt im Wesentlichen zwei Lösungsansätze:There are two main approaches. Zum einen können Sie versuchen, sich die verzögerte Verarbeitung von XStreamingElement zunutze zu machen,One approach is to use the deferred processing characteristics of XStreamingElement. und zum anderen können Sie einen XmlWriter erstellen und dann mithilfe von LINQ to XML Elemente in den XmlWriter zu schreiben.Another approach is to create an XmlWriter, and use the capabilities of LINQ to XML to write elements to an XmlWriter. In diesem Thema werden beide Ansätze besprochen.This topic demonstrates both approaches.

BeispielExample

Das folgende Beispiel baut auf dem Beispiel in Vorgehensweise: Streamen von XML-Fragmenten mit Zugriff auf Headerinformationen (C#) auf.The following example builds on the example in How to: Stream XML Fragments with Access to Header Information (C#).

In diesem Beispiel wird die verzögerte Ausführung von XStreamingElement verwendet, um die Ausgabe zu streamen.This example uses the deferred execution capabilities of XStreamingElement to stream the output. Damit kann auch ein sehr großes Dokument transformiert werden, ohne dass die Speicherbeanspruchung zu groß wird.This example can transform a very large document while maintaining a small memory footprint.

Beachten Sie, dass die benutzerdefinierte Achse (StreamCustomerItem) so geschrieben ist, dass sie über ein Dokument mit Customer-Elementen, Name-Elementen und Item-Elementen verfügt. Diese Elemente werden wie im folgenden Source.xml-Dokument angeordnet.Note that the custom axis (StreamCustomerItem) is specifically written so that it expects a document that has Customer, Name, and Item elements, and that those elements will be arranged as in the following Source.xml document. Eine robustere Implementierung würde jedoch darauf vorbereitet sein, ein ungültiges Dokument analysieren zu können.A more robust implementation, however, would be prepared to parse an invalid document.

Das Quelldokument (Source.xml) sieht wie folgt aus:The following is the source document, Source.xml:

<?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>  
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 are 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)  
{  
    XStreamingElement root = new XStreamingElement("Root",  
        from el in StreamCustomerItem("Source.xml")  
        select new XElement("Item",  
            new XElement("Customer", (string)el.Parent.Element("Name")),  
            new XElement(el.Element("Key"))  
        )  
    );  
    root.Save("Test.xml");  
    Console.WriteLine(File.ReadAllText("Test.xml"));  
}  

Dieser Code erzeugt die folgende Ausgabe:This code produces the following output:

<?xml version="1.0" encoding="utf-8"?>  
<Root>  
  <Item>  
    <Customer>A. Datum Corporation</Customer>  
    <Key>0001</Key>  
  </Item>  
  <Item>  
    <Customer>A. Datum Corporation</Customer>  
    <Key>0002</Key>  
  </Item>  
  <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>  
  <Item>  
    <Customer>Fabrikam, Inc.</Customer>  
    <Key>0008</Key>  
  </Item>  
  <Item>  
    <Customer>Southridge Video</Customer>  
    <Key>0009</Key>  
  </Item>  
  <Item>  
    <Customer>Southridge Video</Customer>  
    <Key>0010</Key>  
  </Item>  
</Root>  

BeispielExample

Das folgende Beispiel baut ebenfalls auf dem Beispiel in Vorgehensweise: Streamen von XML-Fragmenten mit Zugriff auf Headerinformationen (C#) auf.The following example also builds on the example in How to: Stream XML Fragments with Access to Header Information (C#).

In diesem Beispiel wird die Fähigkeit von LINQ to XML genutzt, Elemente in einen XmlWriter zu schreiben.This example uses the capability of LINQ to XML to write elements to an XmlWriter. Damit kann auch ein sehr großes Dokument transformiert werden, ohne dass die Speicherbeanspruchung zu groß wird.This example can transform a very large document while maintaining a small memory footprint.

Beachten Sie, dass die benutzerdefinierte Achse (StreamCustomerItem) so geschrieben ist, dass sie über ein Dokument mit Customer-Elementen, Name-Elementen und Item-Elementen verfügt. Diese Elemente werden wie im folgenden Source.xml-Dokument angeordnet.Note that the custom axis (StreamCustomerItem) is specifically written so that it expects a document that has Customer, Name, and Item elements, and that those elements will be arranged as in the following Source.xml document. Eine robustere Implementierung würde jedoch entweder das Quelldokument mit einer XSD prüfen oder darauf vorbereitet sein, ein ungültiges Dokument analysieren zu können.A more robust implementation, however, would either validate the source document with an XSD, or would be prepared to parse an invalid document.

In diesem Beispiel wird das gleiche Quelldokument (Source.xml) wie im Beispiel oben verwendet.This example uses the same source document, Source.xml, as the previous example in this topic. Es erzeugt auch genau die gleiche Ausgabe.It also produces exactly the same output.

Für das Streamen der XML-Ausgabe wird empfohlen, nicht in einen XStreamingElement zu schreiben, sondern XmlWriter zu verwenden.Using XStreamingElement for streaming the output XML is preferred over writing to an XmlWriter.

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 are 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)  
{  
    IEnumerable<XElement> srcTree =  
        from el in StreamCustomerItem("Source.xml")  
        select new XElement("Item",  
            new XElement("Customer", (string)el.Parent.Element("Name")),  
            new XElement(el.Element("Key"))  
        );  
    XmlWriterSettings xws = new XmlWriterSettings();  
    xws.OmitXmlDeclaration = true;  
    xws.Indent = true;  
    using (XmlWriter xw = XmlWriter.Create("Output.xml", xws)) {  
        xw.WriteStartElement("Root");  
        foreach (XElement el in srcTree)  
            el.WriteTo(xw);  
        xw.WriteEndElement();  
    }  

    string str = File.ReadAllText("Output.xml");  
    Console.WriteLine(str);  
}  

Dieser Code erzeugt die folgende Ausgabe:This code produces the following output:

<Root>  
  <Item>  
    <Customer>A. Datum Corporation</Customer>  
    <Key>0001</Key>  
  </Item>  
  <Item>  
    <Customer>A. Datum Corporation</Customer>  
    <Key>0002</Key>  
  </Item>  
  <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>  
  <Item>  
    <Customer>Fabrikam, Inc.</Customer>  
    <Key>0008</Key>  
  </Item>  
  <Item>  
    <Customer>Southridge Video</Customer>  
    <Key>0009</Key>  
  </Item>  
  <Item>  
    <Customer>Southridge Video</Customer>  
    <Key>0010</Key>  
  </Item>  
</Root>  

Siehe auchSee Also

Advanced LINQ to XML Programming (C#) (Erweiterte LINQ to XML-Programmierung (C#))Advanced LINQ to XML Programming (C#)