XML literals in Visual Basic 9

With Visual Basic 9 (part of the .Net Framework 3.5 and Visual Studio 2008) Microsoft is introducing a very cool new feature to VB: XML literals. In case you don’t have a clue what I’m talking about, allow me to explain.

With XML literals you can create and manipulate XML documents directly from VB. Previously, creating an XML document meant you had three choices: use an XmlDocument, XmlWriter or manipulate strings containing XML directly (I hope nobody actually did that last one). For manipulating an existing document you are pretty much stuck with XmlDocument, or in limited cases XmlReader.

Although the existing options from System.Xml are powerful, they can lack a bit in the usability area. If you are creating a complex document with XmlDocument or XmlWriter, there’s no way you’re going to be able to tell at a glance what this XML is going to look like from looking at the code.

With .Net 3.5, we get the new classes in System.Xml.Linq such as XDocument and XElement which are already a bit easier to manipulate and, more importantly, which play nice with LINQ. And in VB9, we get an extra layer of sugar-coating with XML literals.

But enough talk, let’s look at some code. Let’s say we have a Book class and a collection of books in a List(Of Books) that we want to save in an XML document. For the sake of the example we assume that XML serialization is not suitable in this case for whatever reason. Here’s how you would do this in Visual Basic 8 (.Net 2.0 and 3.0):

Public Sub CreateBookXml(ByVal books As IList(Of Book), ByVal file As String)
    Using writer As XmlWriter = XmlWriter.Create(file)
        writer.WriteStartDocument()
        writer.WriteStartElement("Books")

        For Each book As Book In books
            writer.WriteStartElement("Book")
            writer.WriteAttributeString("author", book.Author)
            writer.WriteString(book.Title)
            writer.WriteEndElement() ' Book
        Next

        writer.WriteEndElement() ' Books
    End Using
End Sub

This is a simple example, but you can easily see how this would get ugly quick if the document gets more complex, and if you've done anything like this in .Net 2.0 you've probably experienced it yourself. Now let’s see how we can tackle the same problem with LINQ and XML literals in VB9:

Public Sub CreateBookXml(ByVal books As IList(Of Book), ByVal file As String)
    Dim bookElements = From book In books _
                       Select <Book author=<%= book.Author %>>
                                  <%= book.Title %>
                              </Book>

    Dim document = <?xml version="1.0" encoding="utf-8"?>
                   <Books>
                       <%= bookElements %>
                   </Books>

    document.Save(file)
End Sub

There’s two things here I want to call your attention to. The first is the XML embedded expressions. Using the <%= %> syntax, which is similar to ASP/ASP.NET (so a lot of VB programmers are already familiar with it), you can add dynamic content to an XML literal. And it’s not just attribute values and element content that you can specify this way: element or attribute names can be made dynamic in exactly the same way.

The second is the type inference; although I don’t specify the type of either bookElements or document, this code was written with Option Explicit On, so these are not Object variables and there’s no late binding going on. Both variables are strongly typed according to the compiler-inferred type based on the expression used to initialize them (bookElements is in fact an IEnumerable(Of XElement), while document is an XDocument). Visual Studio also tells you this when you hover over the variable names, and you get full IntelliSense support.

Not only is this version shorter (only slightly, but the more complex the XML, the bigger the difference), it’s also a lot easier to see at a glance what the result document is going to look like. And because it’s compiler-checked, it’s a lot less easy to screw up; one missing WriteEndElement in the XmlWriter version and the whole thing goes to hell.

So what about using an existing document? If you want to extract information from an existing XML document in VB8 or before, you can do this while reading with XmlReader, or if it’s already loaded in an XmlDocument you can use XPath or traverse the object tree manually. With XML literals we also get the ability to query an XDocument in a very natural way. For example, if you have an XDocument with the books XML we created above, here’s how you would find the titles of all the books from a certain author:

Dim document = XDocument.Load("books.xml")

Dim books = From book In document.<Books>.<Book> _
            Where book.@author = "Frank Herbert" _
            Select book.Value

This gives us an IEnumerable(Of String) with all the book titles. You see how we could easily access elements and attributes in the document from the LINQ expression. And unlike the XPath approach, this is again checked at compile time (although whether it matches the schema will not be checked, since it doesn’t know that at compile time, but at least the syntax is checked) unlike the equivalent XPath expression "/Books/Book[@author='Frank Herbert']" that would not be interpreted until the SelectNodes function is called at runtime. Note that if you want to check not the children, but the descendants of a node, XML literals also provides that by using two dots: "document..<Book>" selects all the Book elements in the document (equivalent to "//Book" in XPath).

Another advantage over XPath is that you don’t actually have to learn XPath. If you know VB9 and LINQ that’s all you need.

So what about C#? C# developers will be sad to hear that C# 3.0 won’t get XML literals. You do get the new XDocument etc. classes for use with LINQ, though. For reference, here’s what both examples would look like in C#:

// First example.
public static void CreateBookXml(IList<Book> books, string file)
{
    var bookElements = from book in books
                       select new XElement("Book",
                                           new XAttribute("author", book.Author),
                                           book.Title);

    var document = new XDocument(new XDeclaration("1.0", "utf-8", null),
                                 new XElement("Books", bookElements));

    document.Save(file);
}

// second example
var document = XDocument.Load("books.xml");
var books = from book in document.Elements("Books").Elements("Book")
            where book.Attribute("author").Value == "Frank Herbert"
            select book.Value;

As you can see, XDocument does offer some advantages over XmlDocument, especially in conjunction with LINQ, but it’s nowhere near as nice as XML literals in VB.

Since everybody is probably tired of this artificial books example that everybody seems to use, in my next post on XML literals (which is available here) I will show a real-life example where I use XML literals to create an RSS feed.

This post was based on Visual Studio 2008 Beta 2. Some of the information may not apply to other versions.

Read more about the new XML features in Visual Basic 9 on MSDN.

Categories: Programming
Posted on: 2007-10-19 12:13 UTC.

Comments

Christian

2007-10-19 14:17 UTC

Nicely done, Sven. Please post the RSS sample asap... :-)

Add comment

Latest posts

Categories

Archive

Syndication

RSS Subscribe