XStreamingElement Class (System.Xml.Linq)

Switch View :
ScriptFree
.NET Framework Class Library
XStreamingElement Class

Represents elements in an XML tree that supports deferred streaming output.

Inheritance Hierarchy

System.Object
  System.Xml.Linq.XStreamingElement

Namespace:  System.Xml.Linq
Assembly:  System.Xml.Linq (in System.Xml.Linq.dll)
Syntax

Visual Basic
Public Class XStreamingElement
C#
public class XStreamingElement
Visual C++
public ref class XStreamingElement
F#
type XStreamingElement =  class end

The XStreamingElement type exposes the following members.

Constructors

  Name Description
Public method Supported by the XNA Framework XStreamingElement(XName) Initializes a new instance of the XElement class from the specified XName.
Public method Supported by the XNA Framework XStreamingElement(XName, Object) Initializes a new instance of the XStreamingElement class with the specified name and content.
Public method Supported by the XNA Framework XStreamingElement(XName, Object[]) Initializes a new instance of the XStreamingElement class with the specified name and content.
Top
Properties

  Name Description
Public property Supported by the XNA Framework Name Gets or sets the name of this streaming element.
Top
Methods

  Name Description
Public method Supported by the XNA Framework Add(Object) Adds the specified content as children to this XStreamingElement.
Public method Supported by the XNA Framework Add(Object[]) Adds the specified content as children to this XStreamingElement.
Public method Supported by the XNA Framework Equals(Object) Determines whether the specified Object is equal to the current Object. (Inherited from Object.)
Protected method Supported by the XNA Framework Finalize Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. (Inherited from Object.)
Public method Supported by the XNA Framework GetHashCode Serves as a hash function for a particular type. (Inherited from Object.)
Public method Supported by the XNA Framework GetType Gets the Type of the current instance. (Inherited from Object.)
Protected method Supported by the XNA Framework MemberwiseClone Creates a shallow copy of the current Object. (Inherited from Object.)
Public method Save(Stream) Outputs this XStreamingElement to the specified Stream.
Public method Supported by the XNA Framework Save(String) Serialize this streaming element to a file.
Public method Supported by the XNA Framework Save(TextWriter) Serialize this streaming element to a TextWriter.
Public method Supported by the XNA Framework Save(XmlWriter) Serialize this streaming element to an XmlWriter.
Public method Save(Stream, SaveOptions) Outputs this XStreamingElement to the specified Stream, optionally specifying formatting behavior.
Public method Supported by the XNA Framework Save(String, SaveOptions) Serialize this streaming element to a file, optionally disabling formatting.
Public method Supported by the XNA Framework Save(TextWriter, SaveOptions) Serialize this streaming element to a TextWriter, optionally disabling formatting.
Public method Supported by the XNA Framework ToString() Returns the formatted (indented) XML for this streaming element. (Overrides Object.ToString().)
Public method Supported by the XNA Framework ToString(SaveOptions) Returns the XML for this streaming element, optionally disabling formatting.
Public method Supported by the XNA Framework WriteTo Writes this streaming element to an XmlWriter.
Top
Remarks

This class allows you to create an XML tree that supports deferred streaming output. You use this class to create an XML tree in a very similar fashion to creating an XML tree using XElement. However, there is a fundamental difference. When you use a LINQ query to specify content when creating an XML tree using XElement, the query variable is iterated at the time of construction of the XML tree, and the results of the query are added to the XML tree. In contrast, when you create an XML tree using XStreamingElement, a reference to the query variable is stored in the XML tree without being iterated. Queries are iterated only upon serialization. This allows you to create larger XML trees while maintaining a smaller memory footprint.

If you are streaming from an input source, such as a text file, then you can read a very large text file, and generate a very large XML document while maintaining a small memory footprint.

Another scenario is that you have a large XML tree that has been loaded into memory, and you want to create a transformed version of the document. If you create a new document using XElement, then you will have two large XML trees in memory upon completion of the transformation. However, if you create the new XML tree using XStreamingElement, then your working set will be effectively cut in half.

Note that when debugging a program that uses XStreamingElement, displaying the value of an object causes its ToString method to be called. This causes the XML to be serialized. If the semantics of your streaming element query are such that the streaming element can only be streamed once, this may cause undesirable behavior in your debugging experience.

Examples

The following example first creates a source XML tree. It then creates a transform of the source XML tree using XElement. This transform creates a new tree in memory. It then creates a transform of the source XML tree using XStreamingElement. This transform doesn't execute the query until the transformed tree is serialized to the console. Its memory usage is less.

C#
XElement srcTree = new XElement("Root",
                       new XElement("Child", 1),
                       new XElement("Child", 2),
                       new XElement("Child", 3),
                       new XElement("Child", 4),
                       new XElement("Child", 5)
                   );

XElement dstTree1 = new XElement("NewRoot",
                        from el in srcTree.Elements()
                        where (int)el >= 3
                        select new XElement("DifferentChild", (int)el)
                    );

XStreamingElement dstTree2 = new XStreamingElement("NewRoot",
                        from el in srcTree.Elements()
                        where (int)el >= 3
                        select new XElement("DifferentChild", (int)el)
                    );

Console.WriteLine(dstTree1);
Console.WriteLine("------");
Console.WriteLine(dstTree2);
Visual Basic
Dim srcTree As XElement = _
        <Root>
            <Child>1</Child>
            <Child>2</Child>
            <Child>3</Child>
            <Child>4</Child>
            <Child>5</Child>
        </Root>

Dim dstTree1 As XElement = _
    <NewRoot>
        <%= From el In srcTree.Elements _
            Where (el.Value >= 3) _
            Select <DifferentChild><%= el.Value %></DifferentChild> %>
    </NewRoot>

Dim dstTree2 As XStreamingElement = New XStreamingElement("NewRoot", _
                From el In srcTree.Elements _
                Where el.Value >= 3 _
                Select <DifferentChild><%= el.Value %></DifferentChild> _
            )

Console.WriteLine(dstTree1)
Console.WriteLine("------")
Console.WriteLine(dstTree2)

This example produces the following output:

<NewRoot>
  <DifferentChild>3</DifferentChild>
  <DifferentChild>4</DifferentChild>
  <DifferentChild>5</DifferentChild>
</NewRoot>
------
<NewRoot>
  <DifferentChild>3</DifferentChild>
  <DifferentChild>4</DifferentChild>
  <DifferentChild>5</DifferentChild>
</NewRoot>

One approach to processing a text file is to write an extension method that streams the text file a line at a time using the yield return construct. You then can write a LINQ query that processes the text file in a lazy deferred fashion. If you then use the XStreamingElement to stream output, you then can create a transform from the text file to XML that uses a minimal amount of memory, regardless of the size of the source text file.

The following text file, People.txt, is the source for this example.

#This is a comment
1,Tai,Yee,Writer
2,Nikolay,Grachev,Programmer
3,David,Wright,Inventor

The following code contains an extension method that streams the lines of the text file in a deferred fashion.

Note Note

The following example uses the yield return construct of C#. Because there is no equivalent feature in Visual Basic 2008, this example is provided only in C#.

C#
public static class StreamReaderSequence
{
    public static IEnumerable<string> Lines(this StreamReader source)
    {
        String line;

        if (source == null)
            throw new ArgumentNullException("source");
        while ((line = source.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        StreamReader sr = new StreamReader("People.txt");
        XStreamingElement xmlTree = new XStreamingElement("Root",
            from line in sr.Lines()
            let items = line.Split(',')
            where !line.StartsWith("#")
            select new XElement("Person",
                       new XAttribute("ID", items[0]),
                       new XElement("First", items[1]),
                       new XElement("Last", items[2]),
                       new XElement("Occupation", items[3])
                   )
        );
        Console.WriteLine(xmlTree);
        sr.Close();
    }
}

This example produces the following output:

xmlLang
<Root>
  <Person ID="1">
    <First>Tai</First>
    <Last>Yee</Last>
    <Occupation>Writer</Occupation>
  </Person>
  <Person ID="2">
    <First>Nikolay</First>
    <Last>Grachev</Last>
    <Occupation>Programmer</Occupation>
  </Person>
  <Person ID="3">
    <First>David</First>
    <Last>Wright</Last>
    <Occupation>Inventor</Occupation>
  </Person>
</Root>

Sometimes you have to transform large XML files, and write your application so that the memory footprint of the application is predictable. 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). Therefore, you should use a streaming technique instead.

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

Even if you use the technique described in How to: Stream XML Fragments with Access to Header Info, if you try to assemble an XML tree that contains the transformed document, memory usage may be too great.

The following example builds on the example in How to: Stream XML Fragments with Access to Header Information.

This example uses the deferred execution capabilities of XStreamingElement to stream the output.

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. A more robust implementation, however, would either validate the source document with an XSD, or would be prepared to parse an invalid document.

The following is the source document, Source.xml:

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

The following code contains a method that uses an XmlReader to stream the source XML. It uses XStreamingElement to stream the new XML.

Note Note

The following example uses the yield return construct of C#. Because there is no equivalent feature in Visual Basic 2008, this example is provided only in C#.

C#
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"));
}

This example produces the following output:

xmlLang
<?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>
Version Information

.NET Framework

Supported in: 4, 3.5

.NET Framework Client Profile

Supported in: 4, 3.5 SP1
Platforms

Windows 7, Windows Vista SP1 or later, Windows XP SP3, Windows Server 2008 (Server Core not supported), Windows Server 2008 R2 (Server Core supported with SP1 or later), Windows Server 2003 SP2

The .NET Framework does not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.
Thread Safety

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
See Also

Reference

Other Resources

Community Content

mcm_ham
All parent elements of collection need to be XStreamingElement
Note XStreamingElement needs to be used for all parent elements in addition to the immediate element containing the collection, otherwise the entire collection will be instantiated in memory. Example:
var doc = new XDocument(
  new XElement("rss",
    new XStreamingElement("channel",
      new XElement("title", "feed title"),
      DBContext.Songs.AsEnumerable().Select(song => new XElement("item",
        new XElement("link", "http://www.website.com/songs.aspx?title=" + Server.UrlEncode(song.Title))
      )
    )
  )
)
doc.Save(Response.Output);
Should be:
var doc = new XStreamingElement("rss",
  new XStreamingElement("channel",
    new XElement("title", "feed title"),
    DBContext.Songs.AsEnumerable().Select(song => new XElement("item",
      new XElement("link", "http://www.website.com/songs.aspx?title=" + Server.UrlEncode(song.Title))
    )
  )
)
doc.Save(Response.Output);
To test if you've done it correct call GC.GetTotalMemory(true) before method call and GC.GetTotalMemory(false) after to check how much memory has increased by.