Categories
Mobile Application Development Programming

Linq to XML

My friend Rob asked me to write a blog post detailing how to use Linq to XML in C# from a beginners point of view. I hope this helps a few people because to be honest I found the documentation I found on the internet quite confusing to begin with.

What does Linq to XML do, and how?

The first thing you want to understand is what Linq to XML is used for, and how it is built to achieve this. Linq to XML is used to create and read XML documents programmatically in C#.  It does this by making XML from instances of classes and making instances of classes from XML.

An example use case for wanting to create an XML document is storing the highscores in a game like sweepy cleaner. The XML file you would want to create would be something like the following. (We’ll use this example throughout)

<?xml version="1.0" encoding="UTF-8"?>
<highscores>
	<highscore>
		<playerName>Danny</playerName>
		<playerRank>1</playerRank>
		<score>28</score>
		<timeAchieved>09/05/2012</timeAchieved>
	</highscore>
	<highscore>
		<playerName>Nick</playerName>
		<playerRank>2</playerRank>
		<score>20</score>
		<timeAchieved>08/05/2012</timeAchieved>
	</highscore>
	<highscore>
		<playerName>Rob</playerName>
		<playerRank>3</playerRank>
		<score>15</score>
		<timeAchieved>02/05/2012</timeAchieved>
	</highscore>
</highscores>

It should be quite clear what this does, but I’ll go through it anyway for people who aren’t familiar with XML. The first line declares the version of XML used and the encoding, fortunately when using Linq this is all done for us, so dont worry about it.

The next line is the first of our elements — an element is just a named area of the document. Think of it as being similar to a class structure in C# in that it holds other elements and attributes, which can be likened to fields. All XML Documents must only have one root element, so in our example we have one called ‘highscores’, which is a good name as it holds highscores. 😛

In between the opening and closing tags for the highscores element we have several ‘highscore’ elements. Think of these as instances of a Highscore class containing a string field called playerName, int fields called playerRank and score and a DateTime field called timeAchieved because as you will soon discover, this is just a text explination of just that — an instance of a class. Anyway, each ‘highscore’ element contains information about a highscore achieved by the player, the actual data we want to retain.

Creating a wrapper class for our Linq Code

Linq creates XML from objects —  sometimes referred to as classes– It also creates instances of objects from XML.  It takes a class, copies the information from it and formats it as XML and can take an XML element a throw all its attributes and elements into an instance of a particular class. Its powerful stuff.

To make an XML file which contains the data of several instances of a class, in this case several highscores, we can make a list of all the objects we wish to save which contains a few more method than an ordinary list. We can do this by making a class which extends a list of the type of object we wish to save. For example to save a list of Highscore objects we would make a class like this:


public class HighscoresList : List<Highscores>

{

        //Linq Stuff

}

whereas if you were trying to save a list of people classes to XML you would have something like the following


public class PeopleList : List<People>

{

      //Linq Stuff
}

This class can now do anything a List could do including adding and removing Highscores or even searching and more! Within this class we will have both our Saving and Loading Linq code.

Saving the objects to XML

Within the HighscoreList class we can create a method called Save which works on each item in list to produce the output in our example. Below is the code. (Sorry for the formatting, I cannot change this, click the “View Source” button to view a better indented version.)

 //Method to save highscore class objects to XML
            public void Save()
            {
                //Create the XML Document
                XElement highscoresXML = new XElement("highscores",
                                            from h in this
                                            select new XElement("highscore",
                                                    new XElement("playerName", h.Name),
                                                    new XElement("playerRank", h.Rank),
                                                    new XElement("score", h.Score),
                                                    new XElement("timeAchieved", h.TimeAchieved)
                                                    ));

                //Save it to Isolated Storage
                using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(XMLDocumentLocation, FileMode.Create, isoStore))
                    {
                        highscoresXML.Save(isoStream);
                    }
                }
            }

As you can hopefully see the Save Method has two clear sections. The creation of the XML and the saving of the XML to a file in Isolated Storage. Isolated Storage is the main way of saving files on Windows Phone, but you could use the same XML creation code and save to a file on Windows using an XMLStreamWriter or even save to an online service.

The first thing we see when creating the xml is an XElement. The first parameter is the name of the element, this maps directly to the element name in XML. So if you have an XElement called highscores, like we do in the above example, it will output the following XML ‘<highscores> </highscores>’. The second parameter is what we want to be contained within that element — referred to as content, this can be an object of any type and will be converted to a string using that objects ToString() method.

As you can remember from earlier every XML Document has to have a root element, Highscores is this element so the object it holds is simply the rest of the XML Document. The clever bit is how we make this. If you’re familiar with SQL database querys you will recognise the basic syntax of the following bit of code, as Linq is also used for database connection the syntax is almost identical, the reason Linq stands for Language INtegrated Query is that its not really C#, its just database commands integrated into the language.

from h in this

This line simply means from each Highscore element, which we will now refer to as h in this HighscoreList. Think of it like a for each loop going round in your list and appending the following code each time it finds a new Highscore Element.

select new XElement(highscore, 

This line means make a new XElement with the name Highscore, and within it have the following elements:

new XElement("playerName", h.Name),
new XElement("playerRank", h.Rank),
new XElement("score", h.Score),
new XElement("timeAchieved", h.TimeAchieved)

Each of these elements is the actual highscore data for that particular highscore. Each has exactly the same syntax as the previous XElements however this time they contain strings rather than other XElements. Remember, H just refers to the Highscore that is currently being written, so the first XElement would output

<playerName>Danny</playerName>

If the highscores playerName field contained the string “Danny”. Linq will loop through all the Highscore instances in the list and write out the XML, so if your list contained 3 highscore instances with the data specified in the example XML the example XML would be written out. We can now save this XML to Isolated Storage, or do whatever else we want to with it.

Reading XML to make Objects

Once we’ve saved our objects to isolated storage or wherever else we will probably want to read them back at some point, otherwise what would be the point!? 🙂 This too is carried out in a method within our HighscoresList class.


            //Method to load highscore class objects from XML
            public void Load()
            {
                XDocument highscoresXML;

                //Load XML From isolated storage
                using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(XMLDocumentLocation, FileMode.Open, isoStore))
                    {
                        highscoresXML = XDocument.Load(isoStream);
                    }
                }

                //Cycle through the XML adding highscore objects to the list each time we find one
                var query = from xElem in highscoresXML.Descendants("highscore")
                            select new Highscore
                            {
                                Name = xElem.Element("playerName").Value,
                                Rank = int.Parse(xElem.Element("playerRank").Value),
                                Score = int.Parse(xElem.Element("score").Value),
                                TimeAchieved = DateTime.Parse(xElem.Element("timeAchieved").Value)
                            };
                this.Clear();
                AddRange(query);
            }

This method is also split into two main parts – The first of which is Loading the actual file from Isolated Storage and the second being converting each Element inthe file into an object which we can use in our program.
The first part is quite simple but also outside the scope of this blog post, you can find out more about Isolated Storage here. The second part cycles through each XElement (referred to as xElem in this code) which is a “Descendant” (a.k.a. a child) of an element called “Highscore”. The following code should explain this.

<?xml version="1.0" encoding="UTF-8"?>
<highscores>
	<highscore>
<!-- We're looking here because all of these elements are children of a "Highscore" -->
		<playerName>Danny</playerName>
		<playerRank>1</playerRank>
		<score>28</score>
		<timeAchieved>09/05/2012</timeAchieved>
	</highscore>
	<highscore>
<!-- And here! -->
		<playerName>Nick</playerName>
		<playerRank>2</playerRank>
		<score>20</score>
		<timeAchieved>08/05/2012</timeAchieved>
	</highscore>
	<highscore>
<!-- And here! -->
		<playerName>Rob</playerName>
		<playerRank>3</playerRank>
		<score>15</score>
		<timeAchieved>02/05/2012</timeAchieved>
	</highscore>
</highscores>

When we find a set of descendants we select them and constructing an instance of a Highscore to put the data in, this is done by the following line of code

select new Highscore

We then fill this temporary instance of Highscore with the data from the child elements of the Highscore Element.

Name = xElem.Element("playerName").Value,
Rank = int.Parse(xElem.Element("playerRank").Value),
Score = int.Parse(xElem.Element("score").Value),
TimeAchieved = DateTime.Parse(xElem.Element("timeAchieved").Value)

This code is fairly self explanatory it takes the Value of each element (a.k.a the content) and puts it in the relevant field of the object. The code will cycle through and make objects for every element in the XML file. Once it has done this we clear the List (to remove old content) and then add our new Highscores from the XML file to it.

this.Clear();
AddRange(query);

And voila, your HighscoreList now contains all the objects contained in your XML file, you can now sort or search it and take elements from it as you would with any other list. I hope this has helped you understand how Linq works and how powerful it can be.

Danny.

14 replies on “Linq to XML”

Bookmarked. I’m constantly remembering and forgetting XML every year :/

One thing – You should probably note that XML is not a database. It’s more for moving data around between applications.

Think of it like JSON, would you use JSON as a database?

I’ll make note of that, though it is interesting how similarly Linq treats XML and databases. I do also use XML instead of databases in some instances (such as saving highscores) just for ease 🙂

Take a look at SQLite, I learned about it earlier this year and wish I’d known about it sooner, it’s really simple to use and is tiny!

This is the book I used when I learned SQLite:

I also found it was pretty good for brushing up on relational database theory!

Worked perfectly for the high-score application. It seems perfect for saving and loading the state of an object, such as a high score collection, without messing around with LINQ.

Have you looked at XQuery before? Its a bit like SQL for XML, you could write an expression like this:

for $item in doc(“highscores.xml”)//highscore
order by $item/@score
return $item

The advantage over LINQ is its easier to build more complex expressions (ordering and grouping different fields for example) and its also pretty fast to evaluate.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.