Scott Hanselman

XLINQ to XML support in VB9

August 28, '07 Comments [6] Posted in Learning .NET | LINQ | Programming
Sponsored By

I started out as a VB guy and spent many years there. However, my love affair with Dim As waned in the last several years and I became a C# guy. Anders is awesome, and the stuff he and his team is doing in C# 3.0 is shiny to say the least. I thought of VB as the Red-headed stepchild of the family. I may have even said things like "what's the point?" but you'll never get me to admit it.

However, I have to say that I'm really impressed with the LINQ to XML stuff in Orcas, to the point where I'd consider writing a library with VB9 just to get this syntax. It's syntactic sugar, to be clear (what isn't, really?) but it's nice enough that it really makes working with XML in VB a pleasure.

Here's a simple example I'm working on...

In C#, using an XmlReader (Yes, I know there are other ways, like XPath, so relax, it's an example), a person wants to count books in an books.xml file very quickly:

int bookcount = 0;
XmlReaderSettings settings = new XmlReaderSettings();

NameTable nt = new NameTable();
object book = nt.Add("book");
settings.NameTable = nt;

settings.IgnoreWhitespace = true;
settings.IgnoreComments = true;

string booksFile = Path.Combine(Request.PhysicalApplicationPath, "books.xml");
using (XmlReader reader = XmlReader.Create(booksFile, settings))
{
    while (reader.Read())
    {
        if (reader.NodeType == XmlNodeType.Element &&
            book.Equals(reader.LocalName)) //A subtle, but significant change!
        {
            bookcount++;
        }
    }
}
Response.Write(String.Format("Found {0} books!", bookcount));

Then you might do something like this to collect the titles of those books and count them, again in C# but now using LINQ to XML:

XDocument booksXML = XDocument.Load(Server.MapPath("books.xml"));        
var books = from book in 
                    booksXML.Descendants("{http://example.books.com}book")
                    select book.Element("{http://example.books.com}title").Value;
Response.Write(String.Format("Found {0} books!", books.Count()));

And that's cool, but here's the same code in VB:

Dim booksXML = XDocument.Load(Server.MapPath("books.xml"))
Dim books = From book In booksXML...<b:book> Select book.<b:title>.Value
Response.Write(String.Format("Found {0} books!", books.Count()))

Notice the syntax around the From statement on the second line. That cool namespace shortcut came from an Imports statement that declared the use of the Xml Namespace, and the prefix. Notice that ... and . are used for the calls to Descendants and Element respectively.

Imports System.Xml.Linq
Imports <xmlns:b="http://example.books.com">

Crazy. Now, let me get some actual objects out of this document. Assume I have an Author class around here.

XDocument booksXML = XDocument.Load(Server.MapPath("books.xml"));

var authors = from book in booksXML.Descendants("{http://example.books.com}author")
            select new Author
            {
                FirstName = book.Element("{http://example.books.com}first-name").Value,
                LastName = book.Element("{http://example.books.com}last-name").Value
            };
foreach (Author a in authors)
{
    Response.Write(String.Format("Author: {1}, {0}<BR/>", a.FirstName, a.LastName));
}

Notice the select new Author statement. I could also have used an anonymous type if I'd liked also. Here's the same in VB:

Dim booksXML = XDocument.Load(Server.MapPath("books.xml"))

Dim authors = From book In booksXML...<books:book> Select New Author _
          With {.FirstName = book.<books:author>.<books:first-name>.Value, _
                .LastName  = book.<books:author>.<books:last-name>.Value}

For Each a As Author In authors
       Response.Write(String.Format("Author: {1}, {0}<BR/>", a.FirstName, a.LastName))
Next

I really like this syntax...LINQ is turning out to not be as scary as I thought. You'll see these examples in a book someday.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Tuesday, August 28, 2007 1:09:16 AM UTC
I just did 2 .Net Rocks TV Shows on LINQ to XML and VB's XML Literals. The first show should be up this week.

Also, you don't have to use the fully qualified name in C#. You can use this syntax:
XNamespace ns = "http://example.books.com";
var authors = from book in booksXML.Descendants(ns +"author")
select new Author
{
FirstName = book.Element(ns + "first-name").Value,
LastName = book.Element(ns + "last-name").Value
};

Don Demsak
Tuesday, August 28, 2007 1:12:01 AM UTC
I like that namespace syntax in VB, thanks for sharing it Scott; gotta get something similar in C#. Though, you could make it a little cleaner like so:

XNamespace ns = "http://example.books.com";

Then use it like this:

var authors = from book in booksXML.Descendants( ns + "author" ) ...

Not as nice, but better.
Anyhow, VB's line continuation _
still _
sucks _
a big one _
and most especially for multi-line LINQ statements.
Nathan Strug
Tuesday, August 28, 2007 1:15:20 AM UTC
Good stuff, thanks Don and Nathan.
Tuesday, August 28, 2007 1:53:21 AM UTC
I remember feeling a little strange about XML literal support when they first rolled it out at PDC '05- I'm fine that they didn't try to stuff this into C#. I'm spending my life in C# 3.0 at the moment, so I haven't tried out the VB9 stuff "for real" (beyond playing with the PDC bits). I'm using LINQ to SQL all over the place, though- as a DataSet hater, I'm surprised at how natural and non-scary it is to let the framework handle O/R mapping for me. It's remarkably transparent, once you learn how the basic expressions get translated to SQL (and you can always write your own SQL and still use the mapping layer if you don't like what comes out). It definitely makes for more rapid development on the middle tier- multi-table inserts are an absolute breeze, and often can be handled in a single DB roundtrip without SP's (hella cool!). The only tricky bit is the null semantics problem- I kinda wish they would've left it the way it was in Beta1 (C# null semantics, not SQL). At least that way, you could trivially mock a LINQ to SQL DataContext- as it is, you have to do a little more work if nulls factor into your selection filters.
Tuesday, August 28, 2007 1:30:36 PM UTC
Interesting too see what Vb9 is trying to achieve, it seems to me that the metaprogramming paradigm will take off once C# 3.0 and Vb 9 launch.
Pop Cataln
Tuesday, August 28, 2007 2:34:35 PM UTC
Skip/Take is another example of VB9's LINQ niceties. If you're returning a paged set of results from a query, it's just a little more logical from the "query as a first class citizen" viewpoint:

In VB:
Dim q = From p In db.Products _
Select p.ProductID, p.ProductName, p.UnitPrice _
Skip 20 _
Take 20

In C#:
var q = from p in db.Products
select p.ProductID, p.ProductName, p.UnitPrice;

q = q.Skip(20).Take(20);
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.