LINQ to XML: An introduction

Why LINQ to XML? This addition was needed; the best way to understand that is to show what you had to do when you wanted to created a simple XML document as the following…

<users>
  <user>
    <name>Alice Wonderland</name>
  </user>
  <user>
    <name>Joe Smith</name>
  </user>
</users>

… in .NET 2.0:

class Program
{
    static void Main(string[] args)
    {
        // create the main xml document.
        XmlDocument xml = new XmlDocument();

        // create and add the users node.
        XmlElement users = xml.CreateElement(“users”);
        xml.AppendChild(users);

        CreateUserNode(xml, users, “Alice Wonderland”);
        CreateUserNode(xml, users, “Joe Smith”);

        // save the results to a temporary file.
        xml.Save(@“C:\Users\Chris\Desktop\output.xml”);
    }

    private static void CreateUserNode(XmlDocument xml, XmlElement users, string name)
    {
        // create and add the user node.
        XmlElement user = xml.CreateElement(“user”);
        users.AppendChild(user);

        // create and add the name node.
        XmlElement nameNode = xml.CreateElement(“name”);
        user.AppendChild(nameNode);

        // create and add the text node.
        XmlText text = xml.CreateTextNode(name);
        nameNode.AppendChild(text);
    }
}

Quite a complex task for such a simple XML file, isn’t it?!

That’s also what the .NET team thought, and for .NET 3.5 they have added the LINQ to XML featureset. LINQ to XML is not only a way to query XML but also a way to compile more easily XML files. Some languages, speaking of VB9, take this concept even a step further and hide the XML creation from the user. Check out the VB9’s XML literals to understand what I mean.

Compiling the XML file from above in C# 3.5 (by using LINQ to XML) looks like this (the key class here is XElement):

static void Main(string[] args)
{
    // create the main xml document.
    XElement xml = new XElement(“users”,
        new XElement(“users”,
            CreateUserNode(“Alice Wonderland”),
            CreateUserNode(“Joe Smith”)));

    // save the results to a temporary file.
    xml.Save(@“C:\Users\Chris\Desktop\output.xml”);
}

private static XElement CreateUserNode(string name)
{
    return new XElement(“user”,
        new XElement(“name”, name));
}

Way shorter, way better! It’s also a lot easier to understand because the items are nested in exactly the same way as they are nested in the final XML file. Creating XML files is finally fun in C#. 8)

Querying XML files

But let us come to the more interesting part now: querying xml files. To allow us to do some proper querying let’s use a more “elaborated” XML file:

<root>
  <user female=“true”>
    <firstName>Emma</firstName>
    <lastName>Heinz</lastName>
    <location>Salzburg</location>
  </user>
  <user female=“false”>
    <firstName>Joe</firstName>
    <lastName>Smith</lastName>
    <location>Vienna</location>
  </user>
  <user female=“true”>
    <firstName>Emma</firstName>
    <lastName>Smith</lastName>
    <location>Vienna</location>
  </user>
</root>

It would be interesting to query for all the females (having the female attribute not null and set to true) and order the result by location. The result is returned as anonymous type and displayed in the console window.

Querying XML files was also possible prior to .NET 3.5 and LINQ to XML. You had to use XPath to do that.

var xml = XElement.Load(@“C:\Users\Chris\Desktop\users.xml”);

// create a query on the xml document.
var query = from u in xml.Elements()
            where u.Attribute(“female”) != null && u.Attribute(“female”).Value == “true”
            orderby u.Element(“location”).Value descending
            select new
            {
                FirstName = u.Element(“firstName”).Value,
                LastName = u.Element(“lastName”).Value,
                Location = u.Element(“location”).Value
            };

foreach (var item in query)
{
    Console.WriteLine(“First: “ + item.FirstName + “; Last: “ + item.LastName + “; Location: “ + item.Location);
}
Console.ReadLine();

The first line of the query asks for all elements underneath the root element: for all “user” elements in our case. Then the query checks if a “female” attribute is present and compares the value of the attribute with the “true” constant. After that the results are ordered by the value in the “location” node. Finally, the result is returned as anonymous type where the values of the “firstName”, “lastName” and “location” node are used to initialize the properties of that type.

LINQ to XML supports all the different query operators that you know from LINQ to Objects or LINQ to SQL.

The current LINQ to XML implementation has still one big problem: you need to specify the names of the nodes as string and can’t use properties for them. That means that you only get errors for invalid nodes during runtime and not during compile time. This could be solved if the XML file would implement a schema and a tool would convert this schema into classes that you could use. This might come in the future but right now nothing is avaible (for LINQ to XML) that does this task.

Another interesting example is to extend the The LINQ to XML concept to read data from another data source and use the new classes to create the XML stream. This might be very interesting if you want to return an ATOM/RSS feed for data that is found in your database or any other data source:

// create a simple list with three elements.
var list2 = new List<Foo>
{
    new Foo { FirstName = “Joe”, LastName = “Smith” },
    new Foo { FirstName = “Emma”, LastName = “Who” },
    new Foo { FirstName = “Andrea”, LastName = “Smith” }
};

// query from the list all with the last name "Smith" and
// create an XML element for each result. The generated
// list of XElements is then added to the root "users" element.
var xml = new XElement(“users”,
            from f in list2
            where f.LastName == “Smith”
            select new XElement(“user”,
                new XElement(“firstName”, f.FirstName),
                new XElement(“lastName”, f.LastName)));

// save the result to the desktop.
xml.Save(@“C:\Users\Chris\Desktop\output.xml”);

Published on Mar 16th, 2008 — Tags: , ,
Comments (3)    digg it!    kick it   

Writing data to an XML file in Visual Basic 9 (Part 3)

Over at Channel 8, Alex Mills asked another question:

As you know my program is built and expanded from the code you wrote, very much appreciated. You designed that when you click edit it opens the XML in a new window. Is it possible that when the listview selected index changes, it automatically loads a couple of nodes of the xml to text boxes on the main form. With one click to be used as a quick reference feature, but I still want to have the seperate editor as I dont want have all the text boxes on the main form. Is this possible?

Everything is possible :) As far as I have understood he needs something that looks like this:

To make this possible we first need to put some controls on the form. :) Next, we need to register (actually, we registered it already in the second example, but I didn’t mentioned it explicitely) the SelectedIndexChanged event of the ListView.

This event is always fired when the ListView’s selected item has changed. That applies also if the selection has been changed to no item being selected anymore. That comes in very handy because we want to clear the textboxes when no item has been selected and fill the textboxes when an item has been selected:

Private Sub ListView1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListView1.SelectedIndexChanged

    ‘ enable or disable the buttons whether an item has been selected.
    Button1.Enabled = (ListView1.SelectedItems.Count > 0)
    Button3.Enabled = (ListView1.SelectedItems.Count > 0)

    ‘ check if an item has been selected
    If ListView1.SelectedItems.Count > 0 Then

        ‘ get the item from the list view.
        Dim path = CType(ListView1.SelectedItems.Item(0).Tag, FileInfo)

        ‘ load the preview data.
        LoadPreview(path.FullName)
    Else

        ‘ clear the preview text boxes.
        ClearPreview()

    End If

End Sub

The first two lines in the method are already here from the second sample. They only enable or disable the “Edit” and “Delete” buttons depending on whether an item has been selected or not. The if statement then checks whether an item has been selected. If one has been selected the selected item’s tag is read (remember, from sample two, the tag contains our file information object) and feed into a new method that is called LoadPreview. LoadPreview is for the most part only a copy of the code that is used in the constructor of the edit form and looks like this:

Private Sub LoadPreview(ByVal path As String)

    ‘ make sure that a path has been given
    If path = Nothing Then
        Throw New ArgumentNullException(“path”)
    End If

    ‘ load the xml document from the given path
    Dim xml = New XmlDocument()
    xml.Load(path)

    ‘ get the different nodes from the xml file and put them in the text boxes.
    ‘ ATTENTION: here should also added a check for the values being Nothing.
    Dim firstName = xml.SelectSingleNode(“//firstname”)
    TextBox1.Text = firstName.InnerText

    Dim lastName = xml.SelectSingleNode(“//lastname”)
    TextBox2.Text = lastName.InnerText

    Dim location = xml.SelectSingleNode(“//location”)
    TextBox3.Text = location.InnerText

End Sub

We get the path as argument of the method and load the XML file. After that we use some XPath to get the values in the XML file and put them in the different textboxes.

The ClearPreview is very simple and looks like this:

Private Sub ClearPreview()

    ‘ set all text boxes to an empty string.
    TextBox1.Text = String.Empty
    TextBox2.Text = String.Empty
    TextBox3.Text = String.Empty

End Sub

The full source code of the sample can be downloaded from here.

Published on Feb 28th, 2008 — Tags: , , ,
Comments (3)    digg it!    kick it   

Writing data to an XML file in Visual Basic 9 (Part 2)

Yesterday I have posted a blog post to answer a question that has been asked in the Channel 8 forums. The question was: “How can I save some values in text fields (on a form) as XML to the disk?”. After having written the blog post the guy asked me if he could also ask me further questions on the topic and I agreed.

So the next questions that came up in the forums are the following (summed up because the post is to long to be quoted):

  • How can I write an XML file to the Windows’ User folder? How do I get this folder?
  • How can I create an own folder with .NET?
  • How can I load the XML file again?
  • How can I show a list with all the files the folder holding my XML files?

Getting the Windows’ User folder is rather easy because the .NET Framework has a class called Environment. The class has a method called GetFolderPath that returns the locations of so called “special folders”. The Windows’ User folder is such a special folder.

Writing the XML file to that given path looks like this:

‘ create the file name. use the GetFolderPath to get the folder of my documents.
Dim path = New DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & “\Waterpoints\”)
‘ if the path doesn’t exist create it.
If Not path.Exists Then
    path.Create()
End If

‘ create the file name from the path and the text box value.
Dim fileName = System.IO.Path.Combine(path.ToString(), firstNameTextBox.Text & “.xml”)

‘ compile the xml.
Dim xml = <?xml version=“1.0″ encoding=“utf-8″?>
          <userrecord>
              <firstname><%= firstNameTextBox.Text %></firstname>
              <lastname><%= lastNameTextBox.Text %></lastname>
              <location><%= locationTextBox.Text %></location>
          </userrecord>

‘ write the xml to the file.
xml.Save(fileName)

This code snippet addresses also the second question. There is a line in the code that checks whether the path already exists. If that’s not the case the Create method on the path is executed to create the directory. .NET makes creating paths really easy :)

The next question was on how to load an XML file. The .NET BCL (Base Class Library) offers a class that’s called XmlDocument. That class allows loading XML files and to search within the loaded files. XmlDocument creates a so called DOM while loading the file. A DOM is a structure that allows navigating and searching.

‘ load the xml document from the given path
Dim xml = New XmlDocument()
xml.Load(path)

‘ get the different nodes from the xml file and put them in the text boxes.
‘ ATTENTION: here should also added a check for the values being Nothing.
Dim firstName = xml.SelectSingleNode(“//firstname”)
firstNameTextBox.Text = firstName.InnerText

Dim lastName = xml.SelectSingleNode(“//lastname”)
lastNameTextBox.Text = lastName.InnerText

Dim location = xml.SelectSingleNode(“//location”)
locationTextBox.Text = location.InnerText

For the search part I’m using XPath. XPath is a handy query languages for XML files that searches within the DOM. The code doesn’t do any error handling. In the case of the XPath queries you sould always check if a result is returned. If the query doesn’t find an item this results in returning an object that is Nothing.

The last question was on how to show a list of XML files in a list view. For that I have created a new form: the MainForm. This form holds only a ListView and 3 buttons: one to create a new XML file, one to edit and one to delete an XML file.

The code to load the list looks like this:

Private Sub LoadFiles()

    ‘ remove all items in the list view.
    ListView1.Items.Clear()

    ‘ get the directory. if it does exist get the files from there.
    Dim directory = New DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & “\Waterpoints”)
    If directory.Exists Then

        ‘ get all xml files in the given directory.
        Dim files = directory.GetFiles(“*.xml”)

        ‘ loop over the files and add them to the list view.
        For Each file In files
            Dim listItem = New ListViewItem(Path.GetFileName(file.FullName))
            listItem.SubItems.Add(file.FullName)
            listItem.Tag = file

            ListView1.Items.Add(listItem)
        Next

    End If
End Sub

This code is a little bit more complex. The complex part is how to fill the ListView with data. The ListView expects a ListViewItem. I set the Text property on that item (via the constructor) to the name of the file (without directories; the Path class offers useful methods to extract different parts of a path) and the first sub item to the FullName of the file (the FullName includes also the directories).

I have also set the file as “tag” of the ListViewItem. That’s handy because later when one of the items is selected I can retrieve the original file object from the tag. You can see the tag as a pocket where you can put one additional object in.

savetoxml2.png

I have also added more code (found in the sample sources) that enables and disabled the edit and delete button depending on whether an item has been selected in the list view.

The full source code of the sample can be downloaded here.

Side note: The example contains no error handling. I haven’t added that because the code gets a lot longer when doing error handling. Error handling should be added to the final application to make it more robust. For an introduction on how to do error handling in VB.NET click here.

Published on Feb 24th, 2008 — Tags: , , ,
Comments (11)    digg it!    kick it   

Writing data to an XML file in Visual Basic 9

Alex Mills asked the following question on Channel 8:

I’m building a application that via the use of a windows form, creates XML files that are also visible and editable by this application. What I dont know is how to set the contents of the texts fields to XML. Really what I need is to develop code that when I press save, saves all the text in the text fields into one XML file.

The first thing that came into my mind are the new XML literals that are available with Visual Basic 9 and I pointed him to Sven’s excellent blog post about them.

After a while I thought I could write this short example for him and upload it somewhere. Nothing easier than doing this directly on my blog :) I have extended the example by a FileSaveDialog instance (added to the Form) that shows a nice “Save as…” dialog when it comes to save the file. The code looks like this:

Imports System.IO

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        ‘ if the user did not click on the OK button return. we can do nothing.
        If SaveFileDialog1.ShowDialog(Me) <> Windows.Forms.DialogResult.OK Then
            Return
        End If

        ‘ get the file name.
        Dim fileName = SaveFileDialog1.FileName

        ‘ compile the xml by using XML literals.
        Dim xml = <?xml version=“1.0″ encoding=“utf-8″?>
                  <userrecord>
                      <firstname><%= firstNameTextBox.Text %></firstname>
                      <lastname><%= lastNameTextBox.Text %></lastname>
                      <location><%= locationTextBox.Text %></location>
                  </userrecord>

        ‘ write the xml to the file.
        xml.Save(fileName)

        ‘ show a message box with the results.
        MessageBox.Show(“The file has been successfully written to: “ + fileName, “Success!”)

    End Sub
End Class

A ZIP with the project can be downloaded from here.

I like XML literals because they make creating XMl files a lot easier. Sad that this feature is not part of C#. I really hope that C# will get something like this. The XDocument class (the VB9 compiler translates XML literals into XDocument calls) is nice but still doesn’t offer the easiness of XML literals. :)

Published on Feb 23rd, 2008 — Tags: , , , ,
Comments (21)    digg it!    kick it