Transformations

With LINQ to XML, you can perform XML transformations using two completely different approaches. The first approach is to use XSLT via the bridge classes, XmlReader and XmlWriter. The second approach is to use LINQ to XML to perform the transformation itself by functionally constructing the target XML document and embedding a LINQ to XML query on the source XML document.

Using XSLT provides the benefit that it is a standard XML technology. Tools already exist to assist with writing, debugging, and testing XSLT transformations. Additionally, because it already exists, you may have XSLT documents and can leverage them in new code using LINQ to XML. There is a world full of existing XSLT documents from which to choose. Additionally, using XSLT for your transformations is just more dynamic. Unlike using the LINQ to XML functional construction approach, you do not have to recompile code to change the transformation. Merely changing the XSLT document allows you to modify the transformation at runtime. Lastly, XSLT is a known technology with many developers having expertise that may be able to assist you. At least in the early days of LINQ, this may not be available if you take the functional construction approach.

Using the functional construction approach does not really buy you much. It does allow you to perform XML transformations knowing nothing more than LINQ to XML. So if you do not already know XSLT, and your transformation needs are modest, this may be a fine approach for you. Also, while functional construction is less convenient than merely modifying an XSLT document, having to recompile code to modify a transformation could be said to add security. Someone cannot simply muck with an outside document to modify the transformation. So for those times when you think you are pushing the limits by using Sarbanes-Oxley as the excuse for not doing something, blame it on the fact that you cannot simply change the transformation without a code overhaul. Or if you are in the medical field and you don't think you can get away with blaming HIPAA one more time, transformation via functional construction may just be the obstacle you need on which to blame a lack of agility.

Transformations Using XSLT

To perform an XML transformation using XSLT, you will utilize the XmlWriter and XmlReader bridge classes that you will obtain from the XDocument classes' CreateWriter and CreateReader methods respectively.

Because the example shown in Listing 9-5 requires a bit of explanation, I will explain it as I go. First, I will specify the transformation style sheet.

Example. Transforming an XML Document with XSLT
string xsl =
  @"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
    <xsl:template match='//BookParticipants'>
        <html>
            <body>
            <h1>Book Participants</h1>
            <table>
                <tr align='left'>
                    <th>Role</th>
                    <th>First Name</th>
                    <th>Last Name</th>
                </tr>
                <xsl:apply-templates></xsl:apply-templates>
            </table>
            </body>
        </html>
    </xsl:template>
    <xsl:template match='BookParticipant'>
        <tr>
            <td><xsl:value-of select='@type'/></td>
            <td><xsl:value-of select='FirstName'/></td>
            <td><xsl:value-of select='LastName'/></td>
        </tr>
    </xsl:template>
  </xsl:stylesheet>";

There is nothing earth-shattering here. I am just specifying some XSL to create some HTML to display my typical book participant XML as an HTML table. Next, I will create my XML document with the book participants:

XDocument xDocument = new XDocument(
  new XElement("BookParticipants",
    new XElement("BookParticipant",
      new XAttribute("type", "Author"),
      new XElement("FirstName", "Joe"),
      new XElement("LastName", "Rattz")),
    new XElement("BookParticipant",
      new XAttribute("type", "Editor"),
      new XElement("FirstName", "Ewan"),
      new XElement("LastName", "Buckingham"))));

This is just my typical XML. Now is where the magic happens. I need to create a new XDocument for the transformed version. Then, from that document, I will create an XmlWriter, instantiate an XslCompiledTransform object, load the transform object with the transformation style sheet, and transform my input XML document into the output XmlWriter:

XDocument transformedDoc = new XDocument();
using (XmlWriter writer = transformedDoc.CreateWriter())
{
  XslCompiledTransform transform = new XslCompiledTransform();
  transform.Load(XmlReader.Create(new StringReader(xsl)));
  transform.Transform(xDocument.CreateReader(), writer);
}
Console.WriteLine(transformedDoc);

Of course, after all that, I display the transformed version of the document. As you can see, I use both bridge classes, XmlWriter and XmlReader, to perform the transformation. Here are the results:

<html>
  <body>
    <h1>Book Participants</h1>
    <table>
      <tr align="left">
        <th>Role</th>
        <th>First Name</th>
        <th>Last Name</th>
      </tr>
      <tr>
        <td>Author</td>
        <td>Joe</td>
        <td>Rattz</td>
      </tr>
      <tr>
        <td>Editor</td>
        <td>Ewan</td>
        <td>Buckingham</td>
      </tr>
    </table>
  </body>
</html>

Transformations Using Functional Construction

While the LINQ to XML API does support XSLT transformations, there are some very effective ways to produce transformations using the LINQ to XML API itself. Logically speaking, a transformation can be as simple as combining a functionally constructed XML tree with an embedded XML query.

Combine functional construction with an embedded XML LINQ query to perform a transformation.


I will explain XML transformations via an example. In many of the examples in the LINQ to XML chapters, I have worked with the following XML tree:

<BookParticipants>
  <BookParticipant type="Author">
    <FirstName>Joe</FirstName>
    <LastName>Rattz</LastName>
  </BookParticipant>
  <BookParticipant type="Editor">
    <FirstName>Ewan</FirstName>
    <LastName>Buckingham</LastName>
  </BookParticipant>
</BookParticipants>

Let's pretend that I need to transform this XML tree to this:

<MediaParticipants type="book">
  <Participant Role="Author" Name="Joe Rattz" />
  <Participant Role="Editor" Name="Ewan Buckingham" / >
</MediaParticipants>

To accomplish this transformation, I will use functional construction with an embedded query. With this approach, you basically functionally construct a new document matching the desired output XML tree structure while obtaining the needed data from the original source XML document by performing a LINQ to XML query. It is the desired output XML tree structure that drives your functional construction and query logic.

Because this task is slightly more complex than some of the previous LINQ to XML examples, I will explain this one as I go. The code is shown in Listing 9-6.

Example. Transforming an XML Document
XDocument xDocument = new XDocument(
  new XElement("BookParticipants",
    new XElement("BookParticipant",
      new XAttribute("type", "Author"),
      new XElement("FirstName", "Joe"),
      new XElement("LastName", "Rattz")),
    new XElement("BookParticipant",
      new XAttribute("type", "Editor"),
      new XElement("FirstName", "Ewan"),
      new XElement("LastName", "Buckingham"))));

Console.WriteLine("Here is the original XML document:");
Console.WriteLine("{0}{1}{1}", xDocument, System.Environment.NewLine);

The previous code simply creates the original source XML document that I am going to transform and displays it. Next, I need to build the new document and root element:

XDocument xTransDocument = new XDocument(
  new XElement("MediaParticipants",

Remember, my desired output XML tree structure is driving my functional construction. At this point, I have the document and root element, MediaParticipants. Next, I need to add the type attribute to the root element:

new XAttribute("type", "book"),

The type attribute and its value do not exist in the source XML document. This would be hard-coded, or possibly configured, in my program logic, which is safe because I already know this code is for a book; otherwise, this code would not be getting called.

Now, I have the MediaParticipants' type attribute handled. Next up, I need to generate a Participant element for each BookParticipant element in the original XML. To do this, I will query the original XML document for its BookParticipant elements:

xDocument.Element("BookParticipants")
        .Elements("BookParticipant")

Now, I have a returned sequence of the BookParticipant elements. Next, I need to generate a Participant element for each BookParticipant element and populate its attributes. I will use projection via the Select operator to construct the Participant elements:

.Select(e => new XElement("Participant",

Next, I construct the two attributes, Role and Name, for the Participant element by getting their values from the BookParticipant element:

new XAttribute("Role", (string)e.Attribute("type")),
          new XAttribute("Name", (string)e.Element("FirstName") + " " +
            (string)e.Element("LastName"))))));

Last, I display the transformed XML document:

Console.WriteLine("Here is the transformed XML document:");
Console.WriteLine(xTransDocument);

Let's see if this outputs what I am looking for:

Here is the original XML document:
<BookParticipants>
  <BookParticipant type="Author">
    <FirstName>Joe</FirstName>
    <LastName>Rattz</LastName>
  </BookParticipant>
  <BookParticipant type="Editor">
    <FirstName>Ewan</FirstName>
    <LastName>Buckingham</LastName>
  </BookParticipant>
</BookParticipants>


Here is the transformed XML document:
<MediaParticipants type="book">
  <Participant Role="Author" Name="Joe Rattz" />
  <Participant Role="Editor" Name="Ewan Buckingham" />
</MediaParticipants>

Wow, that went great! I got the exact output I was looking for. Not bad for using nothing more than LINQ to XML.

Tips

There are a few tips to pass on when it comes to performing XML transformations with the LINQ to XML API. While you may not have a need for these, there is no reason not to point them out.

Simplify Complex Tasks with Helper Methods

There is no requirement that every bit of code needed to perform a transformation or query actually exist in the transformation code itself. It is possible to create helper methods that carry out more complex transformation chores.

Here is some code demonstrating how you can create a helper method to break up a more complex task:

Example. A Helper Method to Transform an XML Document
static IEnumerable<XElement> Helper()
{
  XElement[] elements = new XElement[] {
    new XElement("Element", "A"),
    new XElement("Element", "B")};

  return(elements);
}

In Listing 9-7, I begin the construction of an XML tree. It creates the root node, named RootElement, in the call to the constructor itself. To create the child nodes, it calls a helper method named Helper. It isn't important what the helper method is doing specifically, it just matters that it is helping me build some part of my XML tree and that the call to the method can be embedded in the functional construction of the XML tree.

Example. Using a Helper Method to Transform an XML Document
XElement xElement = new XElement("RootElement", Helper());
Console.WriteLine(xElement);

Here are the results of this code:

<RootElement>
  <Element>A</Element>
  <Element>B</Element>
</RootElement>

Remember, as I discussed in Chapter 7, the XElement constructor knows how to handle IEnumerable<T>, which happens to be the returned data type of my Helper method. How cool is that?

Suppressing Node Construction with null

There may be times when you want to suppress some nodes from being constructed for one reason or another. Perhaps some essential data is missing from the source that causes you to want to omit an element from being created, or perhaps the data is such that you want to skip it.

Back in the "Creating Elements with XElement" section of Chapter 7 when I described the constructor for XElement, I mentioned that you could pass null as an object value for an element's content and that this can be handy when performing transformations. Suppressing node construction is what it is handy for.

As an example, I will first build a sequence of elements. I will then begin constructing a new XML tree based on that sequence. However, if an input element's value is "A", then I don't want to create an output element for that input element. I will pass its value as null to make that happen. The code is in Listing 9-8.

Example. Suppressing Node Construction with null
IEnumerable<XElement> elements =
  new XElement[] {
    new XElement("Element", "A"),
    new XElement("Element", "B")};

XElement xElement = new XElement("RootElement",
  elements.Select(e => (string)e != "A" ? new XElement(e.Name, (string)e) : null));

Console.WriteLine(xElement);

As you can see in the previous code, I do build an input source sequence of elements. I then construct the root element and enumerate through the input source sequence. Then, using the Select operator, as long as the input element's value is not equal to "A", I construct an XElement object using the input element. If the input element's value is equal to "A", I return null. The XElement constructor knows how to handle null; it ignores it. The result is that any element whose value is equal to "A" is eliminated from the output XML tree. You can see I am using the new node value extraction feature by casting the element e as a string in the Select operator's lambda expression.

Here are the results:

<RootElement>
  <Element>B</Element>
</RootElement>

Notice that the element "A" is missing. Of course, there are other ways to implement this same logic without using null. For example, I could have just used the Where operator to filter out the elements whose value is equal to "A". But I wanted to show you the affect of using null in a very simple example.

There are other ways to use this same concept. Perhaps I have some XML to generate that would cause me to have an empty element in some instances that I would prefer not exist. Consider the code in Listing 9-9.

Example. An Example That Generates an Empty Element
IEnumerable<XElement> elements =
  new XElement[] {
    new XElement("BookParticipant",
      new XElement("Name", "Joe Rattz"),
      new XElement("Book", "Pro LINQ: Language Integrated Query in C# 2008")),
    new XElement("BookParticipant",
      new XElement("Name", "John Q. Public"))};

XElement xElement =
  new XElement("BookParticipants",
    elements.Select(e =>
      new XElement(e.Name,
        new XElement(e.Element("Name").Name, e.Element("Name").Value),
        new XElement("Books", e.Elements("Book")))));

Console.WriteLine(xElement);

In the previous code, in the first statement, I generate a sequence of BookParticipant elements, two to be precise. Notice that some of the BookParticipant elements have Book child elements, such as the BookParticipant with the Name child element whose value is "Joe Rattz", and some have no Book elements, such as the BookParticipant whose Name child element is "John Q. Public".

In the second statement, I build an XML tree using the sequence of elements I obtained. In the XML tree, I create an element with the same name as the source sequence, which will be BookParticipant. I then make the participant's name a child element, and then I create a list of Books for each participant. Here is the output from this code:

<BookParticipants>
  <BookParticipant>
    <Name>Joe Rattz</Name>
    <Books>
      <Book>Pro LINQ: Language Integrated Query in C# 2008</Book>
    </Books>
  </BookParticipant>
  <BookParticipant>
    <Name>John Q. Public</Name>
    <Books />
  </BookParticipant>
</BookParticipants>

The XML is just as I would expect based on the code, but notice that the Books element for the second BookParticipant is empty. What if you didn't want an empty Books element if there were no Book elements? You could use null to suppress the Books element as well, with the correct operator. In Listing 9-10, I make a slight change to the code that produces the XML.

Example. An Example That Prevents an Empty Element
IEnumerable<XElement> elements =
  new XElement[] {
    new XElement("BookParticipant",
      new XElement("Name", "Joe Rattz"),
      new XElement("Book", "Pro LINQ: Language Integrated Query in C# 2008")),
    new XElement("BookParticipant",
      new XElement("Name", "John Q. Public"))};

XElement xElement =
  new XElement("BookParticipants",
    elements.Select(e =>
      new XElement(e.Name,
        new XElement(e.Element("Name").Name, e.Element("Name").Value),
        e.Elements("Book").Any() ?
							          new XElement("Books", e.Elements("Book")) : null)));

Console.WriteLine(xElement);

The significant change in the previous code is in bold. Instead of just creating a Books element and specifying all the existing Book elements as its content, I use the Any Standard Query Operator combined with the ternary operator (if ? then : else) to create the Books element only if there are in fact any Book elements. If there are no Book elements, the ternary operator returns null, and the XElement constructor knows to just ignore null, thereby eliminating the creation of the Books element. This can be very handy. Here are the results after the modification:

<BookParticipants>
  <BookParticipant>
    <Name>Joe Rattz</Name>
    <Books>
      <Book>Pro LINQ: Language Integrated Query in C# 2008</Book>
    </Books>
  </BookParticipant>
  <BookParticipant>
    <Name>John Q. Public</Name>
  </BookParticipant>
</BookParticipants>

As you can see, the second BookParticipant element no longer has an empty Books element, as it did in the previous example.

Handling Multiple Peer Nodes While Remaining Flat

Sometimes when making an XML transformation, you know exactly how many of each type of output element you are going to want. But what happens if there are several known elements as well as a variable number of repeating elements all at the same level in the tree for each entry in the source XML? Let's say I have the following XML:

Example. What I Want My Source XML to Look Like
<BookParticipants>
  <BookParticipant type="Author">
    <FirstName>Joe</FirstName>
    <LastName>Rattz</LastName>
    <Nickname>Joey</Nickname>
    <Nickname>Null Pointer</Nickname>
  </BookParticipant>
  <BookParticipant type="Editor">
    <FirstName>Ewan</FirstName>
    <LastName>Buckingham</LastName>
  </BookParticipant>
</BookParticipants>

What if I want to flatten the structure so that the BookParticipants root node only contains repeating sets of FirstName, LastName, and Nickname elements, instead of those elements being contained in a child BookParticipant element? I would like for the target XML to look like this:

Example. What I Want the XML to Look Like After Transformation
<BookParticipants>
  <!-- BookParticipant  -->
  <FirstName>Joe</FirstName>
  <LastName>Rattz</LastName>
  <Nickname>Joey</Nickname>
  <Nickname>Null Pointer</Nickname>
  <!-- BookParticipant  -->
  <FirstName>Ewan</FirstName>
  <LastName>Buckingham</LastName>
</BookParticipants>

The comments are not necessary, but they make it easier for a human to know what they are looking at. Plus, without them, if you looked farther down in the list, it might be confusing as to whether the FirstName or LastName comes first, causing a human to think that there is a BookParticipant named Ewan Rattz when there really isn't.

Because this example is more complex, I will explain it as I go. Let's take a look at the example code in Listing 9-11 to make this transformation.

Example. Handling Multiple Peer Nodes While Maintaining a Flat Structure
XDocument xDocument = new XDocument(
  new XElement("BookParticipants",
    new XElement("BookParticipant",
      new XAttribute("type", "Author"),
      new XElement("FirstName", "Joe"),
      new XElement("LastName", "Rattz"),
      new XElement("Nickname", "Joey"),
      new XElement("Nickname", "Null Pointer")),
    new XElement("BookParticipant",
      new XAttribute("type", "Editor"),
      new XElement("FirstName", "Ewan"),
      new XElement("LastName", "Buckingham"))));

Console.WriteLine("Here is the original XML document:");
Console.WriteLine("{0}{1}{1}", xDocument, System.Environment.NewLine);

At this point, I have built the source XML tree and displayed it. It does indeed match the XML I specified previously as the source. Now I just have to transform the source XML:

XDocument xTransDocument = new XDocument(
  new XElement("BookParticipants",
    xDocument.Element("BookParticipants")
      .Elements("BookParticipant")

Here is where the challenge occurs. I am about to use projection via the Select operator to create an object in which I will contain the comment, first name, last name, and any nicknames. But what object type should I create? I could create an element and make the comment, first name, and the remainder child elements of it, but that would expand the XML tree by adding a level. So I must create something that will not add a level to the XML tree. An array of objects will work for this, because in C# 3.0, an array implements IEnumerable<T>, thereby making the array of objects work just like a sequence. As you hopefully recall from Chapter 7, when an IEnumerable is passed into an XElement constructor as its content, the sequence is enumerated and each object in the sequence is applied to the element being constructed. I will use the C# 3.0 collection initialization features to populate that array with the comment, first name, last name, and any nicknames:

.Select(e => new object[] {
        new XComment(" BookParticipant "),
        new XElement("FirstName", (string)e.Element("FirstName")),
        new XElement("LastName", (string)e.Element("LastName")),
        e.Elements("Nickname")})));

Console.WriteLine("Here is the transformed XML document:");
Console.WriteLine(xTransDocument);

At this point, I have projected an array containing a comment, a FirstName element, a LastName element, and however many Nickname elements there are in the source XML. Finally, I display the transformed XML document.

This example is actually quite complex. Notice that my array of objects includes an XComment object, two XElement objects, and an IEnumerable<XElement>. By projecting a newly instantiated array as the return value of the Select operator, a sequence of object[], IEnumerable<object[]>, is being returned as the content of the newly constructed BookParticipants element.

In this case, each object in that sequence is an array of objects, where the array contains the comment, FirstName and LastName elements, and the sequence of Nickname elements. Because, as I just mentioned, an array of objects does not inject a level into the XML tree, the array adds its elements directly into the BookParticipants element.

This may be confusing; so let's take a look at the results:

Here is the original XML document:
<BookParticipants>
  <BookParticipant type="Author">
    <FirstName>Joe</FirstName>
    <LastName>Rattz</LastName>
    <Nickname>Joey</Nickname>
    <Nickname>Null Pointer</Nickname>
  </BookParticipant>
  <BookParticipant type="Editor">
    <FirstName>Ewan</FirstName>
    <LastName>Buckingham</LastName>
  </BookParticipant>
</BookParticipants>


Here is the transformed XML document:
<BookParticipants>
  <!-- BookParticipant -->
  <FirstName>Joe</FirstName>
  <LastName>Rattz</LastName>
  <Nickname>Joey</Nickname>
  <Nickname>Null Pointer</Nickname>
  <!-- BookParticipant -->
  <FirstName>Ewan</FirstName>
  <LastName>Buckingham</LastName>
</BookParticipants>

The transformed XML matches the specification exactly. Bravo! The real nifty part of this example is how I project an array of objects, a non-XML class, to create peer XML elements without inflicting a level of XML to the tree.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset