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