LINQ Part 2 - Filtering & Sorting Using Data from the Database
LINQ Part 4 - What's behind a query expression?

LINQ Part 3 - Filtering and Sorting XML

LINQ Part 1 was about filtering and sorting object lists. LINQ Part 2 had the same result with information from the database. In part 3 I'm showing how to get the same information from XML data.

The XML data I'm using is similar to the data from the database from part 2:

<Racers>
  <Racer>
    <Name>Fernando Alonso</Name>
    <Car>Renault</Car>
    <Wins>9</Wins>
  </Racer>
  <Racer>
    <Name>Giancarlo Fisichella</Name>
    <Car>Renault</Car>
    <Wins>2</Wins>
  </Racer>
  <!-- ... -->

</Racers>

The requirement now is - as it was before - to filter the racers by selecting only the racers that have more than three wins, and sorting the result by the number of wins.

Doing this the "traditional" way, it is possible to filter the racers using an XPath expression: /Racers/Racer[Wins > 3]. However, this doesn't do a sort. The resulting list can be sorted by sorting the object list. Here I've chosen to filter and sort using XML features: XSLT. The select attribute specifies the XPath filter expression, and the resulting elements are sorted according to the xsl:sort element:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <Racers>
      <xsl:apply-templates select="/Racers/Racer[Wins > 3]">
        <xsl:sort select="Wins" data-type="number" order="descending"/>
      </xsl:apply-templates>

    </Racers>
  </xsl:template>
  <xsl:template match="Racer">
    <Racer>
      <Name>
        <xsl:value-of select="Name"/>
      </Name>
      <Wins>
        <xsl:value-of select="Wins"/>
      </Wins>
    </Racer>
  </xsl:template>
</xsl:stylesheet>

Using the XslCompiledTransform (.NET 2.0) class, the XML document is transformed written to a memory stream:

XslCompiledTransform transform = new XslCompiledTransform();
   transform.Load("Racers.xslt");

MemoryStream stream = new MemoryStream();
XmlTextWriter output = new XmlTextWriter(stream, Encoding.UTF8);
transform.Transform("Racers.xml", output);

The XML data in the memory stream is loaded inside the XmlDocument class, and here all racers are iterated with a foreach:

XmlDocument resultDoc = new XmlDocument();
stream.Seek(0, SeekOrigin.Begin);
resultDoc.Load(stream);

foreach (XmlNode racer in resultDoc.SelectNodes("Racers/Racer"))
{
    Console.WriteLine("{0} {1}", racer.ChildNodes[0].InnerText,
         racer.ChildNodes[1].InnerText);
}

Now to a new version: how can the same result be achived using XLINQ?

XElement is a new class that represents an XML element but also allows loading XML data with the static Load method. Now similar to accessing data from objects and the database, with the XLINQ query expression from where orderby select, data can be filtered and sorted. racers.Elements("Racer") accesses all Racer elements. r.Elements("Wins") accesses the Wins element that is a child of a Racer element. Because the Wins element is optional, a cast to a nullable int int? is performed.

var racers = XElement.Load("Racers.xml");

var winners = 
   from r in racers.Elements("Racer")
   where (int?)r.Element("Wins") > 3
   orderby (int?)r.Element("Wins") descending
   select (string)r.Element("Name") + " " + (string)r.Element("Wins");

foreach (string name in winners)
{
    Console.WriteLine(name);
}

Comparing how to filter and sort XLINQ with XSLT, XLINQ has the great advantage that it uses the same principles as accessing object lists (LINQ) and accessing data from the database (DLINQ). There's no need to learn a complete different syntax as XSLT.

Christian

Comments

Feed You can follow this conversation by subscribing to the comment feed for this post.

vinnie

awesome series, thanks. seeing LINQ in action helps me understand and appreciate the new C# 2.0 features. btw... i think u mean XLINQ up there in your example, not XQuery :)

Christian Nagel

Vinnie, thanks for your comments :-) Of course I meant XLINQ. Corrected.

Keith J. Farmer

One of the things I've disliked about the preview of XLinq is the explicit casting that is constantly being done upon accessing XElement and the like. I think it would be a good exercise to implement and demonstrate a typed XDocument that implements a schema. It shouldn't be difficult...

Christian Nagel

Keith, in a future entry of this series I will show typed XML. This is supported by VB9. I hope for C# 3.0, too.

Jay R. Wren

since LINQ exposes the expression tree, it SHOULD be possible to actually write something to read this expression tree and output XSLT! So you could write LINQ, and output equivalent XSLT.

The comments to this entry are closed.