Recipe 3 showed one way to work with mixed XML content using the XML Descendant axis property and the ReplaceWith method. This is one way to do an identity transform and we’ll look at another method in a later post. There is a key capability not mentioned in Recipe 3 that you will need for fully-functioning identity transforms. That is the concept of copying the inner XML of an element. You get the inner XML of an element from the Nodes property.
Recipe 3 transformed e-mail objects from our AdventureWorks sample document into hyper-links in an HTML document and preserved the original formatting by performing an identity transform. To keep things simple in Recipe 3, however, I left out the fact that the schema for the <eMailAddress> element (found in the ContactTypes.xsd schema file) allows for both an <eMail> element that contains the e-mail address, and also a <SpecialInstructions> element for additional instructions such as phone numbers to use in place of an e-mail, when to call, and so on. The <SpecialInstructions> element can contain any of the other elements identified in ContactTypes.xsd. To include all of the information for an <eMailAddress> element, we must also include any special instructions. (Note: You can download the XML document and related schemas from the Recipe 1 post).
Transforming the <eMail> element from an e-mail address is simple because the <eMail> element contains only the e-mail address value. However, transforming the <SpecialInstructions> element is not as simple because the element value can contain sub- elements as well as values. If you embedded the Value of a <SpecialInstructions> element, you would get the text found within the element, but any XML found would be stripped out. This is also referred to as the inner text of an element.
If you’re familiar with the classes in the System.Xml namespace, you will immediately recognize this behavior. The solution is to embed the inner XML of the element rather than the inner text (the Value property). While the System.Xml classes expose both InnerText and InnerXml properties, LINQ to XML does not. Inner text is returned from the Value property, and inner XML is returned from the Nodes property. Why the difference? The InnerXml property returns a string of XML, which would have to be re-parsed into XML objects. The Nodes property returns a collection of LINQ to XML objects, which you can manipulate or embed as a whole into XML Literals making the Nodes property a powerful tool for transforming XML.
So, with all of this in mind, let’s look at both the Value (inner text) and Nodes (inner XML) properties in an example. Here we have a function (template), TransformEmail, that transforms the XML source into HTML. The e-mail address is retrieved using the Value property and also a call to the GetSpecialInstructions function to embed the HTML for any special instructions that may be included. The GetSpecialInstructions method returns HTML markup and the inner XML of the <SpecialInstructions> element as it can contain mixed markup including text, or other XML.
Private Sub TransformEmail(ByVal email As XElement)
Dim emailHtml = <div class=“Email“>
<a href=<%= “mailto:” & email.<act:eMailAddress>.Value %>>
<%= email.<act:eMailAddress>.Value %>
<%= GetSpecialInstructions(email.<act:SpecialInstructions>) %>
Private Function GetSpecialInstructions( _
ByVal instructions As IEnumerable(Of XElement)) As IEnumerable(Of XElement)
If instructions IsNot Nothing Then _
Return From instruction In instructions _
Select <span class=“SpecialInstructions“>
<%= instruction.Nodes %>