VB XML Cookbook, Recipe 3: Identity Transforms (Doug Rothaus)

An identity transform in XSLT does just what the name implies: replace the identity of an element or attribute with a new identity. Identity transforms are especially critical when dealing with XML markup that is less rigid in its structure. Consider a documentation structure for news articles. Whenever a title is referred to within an article, it is highlighted with italics. However, the XML schema for the article does not specify italics, it specifies that the term is some type of title so that different transforms can handle the term in their own fashion. When you transform XML such as this into a readable format, such as HTML, you need to preserve the structure of the text around the highlighted term so that the original intent remains intact. That is…

<Paragraph sequenceID=”1”>This week a remastered version of the movie <title type=”movie”>Raider’s of the Lost Ark</title> was released.</Paragraph>

becomes…

<p>This week a remastered version of the movie <i>Raider’s of the Lost Ark</i> was released.</p>

In a browser you see…

This week a remastered version of the movie Raider’s of the Lost Ark was released.

(Note: I have no idea if they are really releasing a remastered version of Raider’s of the Lost Ark, I just typed the first thing that popped into my head).

You can perform an identity transform using Visual Basic and XML Literals by combining XML Axis properties and the ReplaceWith method for LINQ to XML objects.

Let’s look at a more complete example. In the AdventureWorks contacts source document that we used in previous recipes (you can download the XML document and related schemas from the Recipe 1 post), there is an <AdditionalContactInfo> element that contains information about the contact such as phone numbers, shipping and billing addresses, and so on. To keep things simple, we’ll just look at the <eMail> element. The <eMail> element can show up in a number of places in the contents of the <AdditionalContactInfo> element. As a result, you can use the XML Descendant axis property discussed in Recipe 2 to retrieve all of the references to <eMail> elements. You can then iterate through the query results and call the ReplaceWith method for each <eMail> element and replace it with a new identity. For example:

  Dim emails = (From email In _

    xmlDoc.<Contacts>.<Contact>.<aci:AdditionalContactInfo><act:eMail>).ToList()

 

  For Each email In emails

    TransformEmail(email)

  Next

In this example, the output is HTML and the <eMail> element is replaced with an anchor element that specifies a mailto: link to the e-mail address. The previous code snippet passes the <eMail> XElement object to the function TransformEmail, which does the actual replacing.

  Private Sub TransformEmail(ByVal email As XElement)

    Dim emailHtml = <div class=Email>

                      <a href=<%= “mailto:” & email.<act:eMailAddress>.Value %>>

                        <%= email.<act:eMailAddress>.Value %>

                      </a>

                    </div>

 

    email.ReplaceWith(emailHtml)

  End Sub

 

A simple class that creates the HTML document using these samples is shown here.

Imports <xmlns=http://SampleSchema/AWContacts>

Imports <xmlns:aci=http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactInfo>

Imports <xmlns:act=http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes>

 

Public Class Recipe3

 

  Public Function GetContactsHtml(ByVal xmlDoc As XDocument) As XElement

    ‘ Replace e-mail address tags with mailto links.

    Dim emails = (From email In xmlDoc.<Contacts>.<Contact>.<aci:AdditionalContactInfo><act:eMail>).ToList()

 

    For Each email In emails

      TransformEmail(email)

    Next

 

    ‘ Create the HTML document

    Return <html>

             <body>

               <table border=1>

                 <%= From contact In xmlDoc.<Contacts>.<Contact> _

                     Select <tr>

                              <td valign=top>

                                <%= contact.<FirstName>.Value & ” “ & contact.<LastName>.Value %>

                              </td>

                              <td valign=top>

                                <%= contact.<aci:AdditionalContactInfo> %>

                              </td>

                            </tr> _

                 %>

               </table>

             </body>

           </html>

  End Function

 

 

  Private Sub TransformEmail(ByVal email As XElement)

    Dim emailHtml = <div class=Email>

                      <a href=<%= “mailto:” & email.<act:eMailAddress>.Value %>>

                        <%= email.<act:eMailAddress>.Value %>