Visual Basic 9.0 is a language that implements a particular
syntax support for LINQ to XML, consisting of XML literals and late binding
over XML. To describe these features, we will use some classes that are part of
LINQ to XML: XDocument, XElement,
and XAttribute. ”
For the purpose of describing XML support, it is sufficient to know that these
classes represent an XML document, element, and attribute, respectively.
XML Literals
In Visual Basic 9.0, an XML literal is
considered an expression. If you want to assign a value to an object
representing an XML tree, you can simply write that value as an assigned
expression, as shown in Listing 3-23.
Listing 3-23: XML
literal used as a constant
Dim ourBook As XElement
ourBook = _
<Book Title="Introducing LINQ">
<Author>Marco Russo</Author>
<Author>Paolo Pialorsi</Author>
</Book>
We have assigned to ourBook an
XElement instance named Book that has an
attribute Title containing “Introducing LINQ” and two
inner Author elements containing our names.
The code in Listing 3-23 is
translated by the compiler into the following calls:
|
Important |
As we said before, XML literals are expressions in Visual
Basic 9.0. These expressions do not require a line continuation when defined
across multiple lines. More important, either an underscore character or a
comment in an XML literal is considered part of the XML and not a line
continuation or a comment. This is a big exception against Visual Basic syntax.
The end of the expression is defined by the closing tag matching the initial
tag.
|
As usual, we can infer the variable type by placing the initial
assignment into the declaration. In the next assignment, we can also see an XML
literal defined in a single line:
It is important to understand that XML literals are converted into
method calls by the compiler. The compiler expects a valid XML syntax. An
invalid XML literal produces a compiler error. As you can see in
Listing 3-24, the XML literal assigned to a has
an invalid Title attribute (it’s missing a "), and the
one assigned to b has a missing closing tag (it should
be </Book> instead of <Book>).
Listing 3-24: Invalid
XML literals
Dim a = <Book Title="Invalid Title></Book> ' Error
Dim b = <Book Title="Good Title"><Book> ' Error
Visual Basic 9.0 leverages XML literals, thereby enabling the calls
for other expressions. In other words, an XML literal can be an expression that
will be evaluated at execution time and not only a constant in the code. For
example, imagine that we want to generate an XML literal dynamically for a book
and use a string to assign the book title attribute. To do that, we need to
“break” the XML literal with a regular Visual Basic 9.0 expression, using the
special <%= and %> tags.
The code in Listing 3-25 shows how to
assign attributes and element content with regular Visual Basic 9.0
expressions.
Listing 3-25: XML
literal in a dynamic expression
Dim bookTitle = "Introducing LINQ"
Dim author1 = "Marco Russo"
Dim author2 = "Paolo Pialorsi"
Dim book = _
<Book Title=<%= bookTitle %>>
<Author><%= author1 %></Author>
<Author><%= author2 %></Author>
</Book>
|
Warning |
Leave a space after <%= tags and
before %> tags; otherwise, the compiler cannot
interpret the expression correctly.
|
The <%= and %>
tags define a “hole” into an XML literal that embeds an expression, which is
evaluated and substituted into the hole at execution time. These holes are
placeholders equivalent to the parameters of an XElement,
XAttribute, or XDocument constructor.
You can place into holes an expression that is valid in those contexts.
For example, we can define in a dynamic way not only the element
and attribute content, but also their names, as shown in
Listing 3-26. We also use a string concatenation to assign the
Authors element, combining two strings with our names, divided by a
comma.
Listing 3-26: Dynamic
XML tags in an XML literal
Dim bookTitle = "Introducing LINQ"
Dim author1 = "Marco Russo"
Dim author2 = "Paolo Pialorsi"
Dim tagBook = "Book"
Dim attrName = "Title"
Dim tagAuthors = "Authors"
Dim book = _
<<%= tagBook %> <%= attrName %>=<%= bookTitle %>>
<<%= tagAuthors %>><%= author1 & ", " & author2 %></>
</>
In our opinion, the example in Listing
3-26 is not the best way to use XML literals. Filling the code with
angular brackets and “holes” is not the best way to make code readable. The
same book assignment is more explicit and clear if
written with the code shown in Listing 3-27
(for the sake of brevity, we skipped variable declarations).
Listing 3-27: Dynamic
XML tags in explicit method calls
Dim book = _
New XElement(tagBook, _
New XAttribute(attrName, bookTitle), _
New XElement(tagAuthors, author1 & ", " & author2))
|
Note |
We are not saying that you should not use expressions to
assign a tag name in an XML literal. Our intention is to emphasize that
expressions embedded in XML literals are convenient when the resulting XML
structure is immediately understandable. At the extreme, you could define an
XML literal without any constant inside (as we did before), but we do not think
this is significantly more readable than regular method calls.
|
If you use an embedded expression to define XML element names, you
have to be careful with the syntax required for the closing tag. In such a
case, the closing tag must not have the tag name inside. As you can see in
Listing 3-28, the closing tag is only </>,
without the dynamic tag name defined by the tagElement variable.
Listing 3-28: Closing
tag for dynamic XML tags
Dim tagElement = "Description"
Dim sample = <<%= tagElement %>>Sample element</>
A hole in an XML literal can embed just one expression. Because an
XML literal is an expression, you can write this apparently useless syntax:
In this case, the Publisher element is an
XML literal embedded into a hole of the external Book element,
another XML literal. This example is useful for observing an important syntax
detail. The <%= tag must have the evaluated
expression in the same line; otherwise, it requires a line continuation. The
%> tag must be in the same line as the end of the evaluated
expression; otherwise, we need a line continuation in the preceding line.
In other words, the following code is not valid:
But we can separate code in different lines by using line
continuation, as in the following sample. Note that line continuation is always
external to XML literals.
If you want to put more expressions into a single hole, you must
define an array of elements. For example, to put two separate Author
elements into a single hole, we can enclose a list of XML literals between
brackets, as shown in Listing 3-29.
Note the use of a line continuation character after the first element in the
array initializer.
Listing 3-29: Nested
list of XML literals
Dim book = _
<Book Title="Introducing LINQ">
<%= { <Author>Marco Russo</Author>, _
<Author>Paolo Pialorsi</Author> } %>
</Book>
We just used an array to put a list of Author
elements into a Book element. Therefore, we can
leverage other methods that generate an IEnumerable object
to put a list of XML elements into another XML element.
In the statements shown in Listing
3-30, we use a LINQ query to get the list of authors from an array of
people. As you can see, an XML literal can embed a query expression that
returns a sequence of XML literals, which are built with another embedded
expression that references each row returned from the query.
Listing 3-30: Query
embedded into an XML literal
Dim team = {New {Name := "Marco Russo", Role := "Author"}, _
New {Name := "Paolo Pialorsi", Role := "Author"}, _
New {Name := "Roberto Brunetti", Role := "Reviewer"}}
Dim book = _
<Book Title="Introducing LINQ">
<%= From person In team _
Where person.Role = "Author" _
Select <Author><%= person.Name %></Author> %>
</Book>
By combining LINQ syntax and XML literals, you can generate
simple and complex XML data structures containing query results in an easy and
efficient way.
Late Binding over XML
When you want access to XML data, you probably need to
navigate into an object tree that represents the hierarchical structure of the
XML document. Visual Basic 9.0 offers some syntax that simplifies this kind of
operation-that is, late-binding operations over XML.
We start by considering an XML list of movies. Each movie item must
have one Title (as an attribute) and one
Director (as an element), plus one list of genres for each movie. The
following code shows part of the initialization in our sample code:
The first dedicated XML syntax that we show is the child
axis. If we write movie.<Genre>, we get
all the genres of the chosen movie, as we can see in
Listing 3-31.
Listing 3-31: Child
axis
Dim fightClub = _
(From movie In movies.<Movie> _
Where movie.@Title = "Fight Club" _
Select movie).First()
' Get the first genre
Dim firstGenre = fightClub.<Genre>(0).Value
' Corresponds to: firstGenre = fightClub.Elements("Genre")(0).Value
Console.WriteLine("First = {0}", firstGenre)
' Display all genres
' Corresponds to: fightClub.Elements("Genre")
For Each g In fightClub.<Genre>
Console.WriteLine(g.Value)
Next
If the query provides only one row, or if we are interested only in
the first row of results, we can access the first element of the collection.
For example, fightClub.<Genre>(0) allows access
to a corresponding XElement instance. If we need the
value of an element or an attribute, we need to read the Value
property. However, this syntax might return more than one row. In such a case,
a loop that iterates over all rows in the sequence might access all of them.
The child axis syntax is translated into a call to Elements,
specifying the name of the element as an argument. For syntax details, refer to
the comments in Listing 3-31.
You can access an attribute through the attribute
axis. If you write fightClub.@Title, you get a
string with the value of the attribute, as shown in
Listing 3-32.
Listing 3-32: Attribute
axis
' Display the Title attribute of Fight Club movie
Console.WriteLine(fightClub.@Title)
' Corresponds to: Console.WriteLine(fightClub.Attribute("Title").Value)
The attribute axis syntax is translated into a call to
Attribute, specifying the name of the attribute as an argument.
The last operation introduced here is the descendants
axis. It allows you to get all children of an element, regardless of
their position in the hierarchy. You can see in
Listing 3-33 that the syntax is similar to the one for the child axis,
but there are three dots instead of one.
Listing 3-33: Descendants
axis
' List of Directors
Dim directors = movies...<Director>
For Each director In directors
Console.WriteLine(director)
Next
In this case, the output of Listing
3-33 (the descendants axis) shows possible duplicates. We could use the
Distinct operator to “clean up” the output.