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

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

  • Comments 4

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 %>

                      </a>

                    </div>

    email.ReplaceWith(emailHtml)

  End Sub

End Class

 

In XSLT, the same transform would look like the following:

<?xml version='1.0'?>

<xsl:stylesheet

  version="1.0"

  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

  xmlns:aw="http://SampleSchema/AWContacts"

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

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

  <xsl:output method="html" indent="yes"/>

  <xsl:template match="aw:Contacts">

    <html>

       <body>

         <table border="1">

           <xsl:apply-templates select="aw:Contact" />

         </table>

      </body>

    </html>

  </xsl:template>

 

  <xsl:template match="aw:Contact">

    <tr>

      <td valign="top">

        <xsl:value-of select="aw:FirstName"/>

        <xsl:text> </xsl:text>

        <xsl:value-of select="aw:LastName"/>

      </td>

      <td valign="top">

        <xsl:apply-templates select="aci:AdditionalContactInfo" />

      </td>

    </tr>

  </xsl:template>

 

  <xsl:template match="aci:AdditionalContactInfo">

    <div class="AdditionalInfo">

      <xsl:copy>

        <xsl:apply-templates select="@* | node()" />

      </xsl:copy>

    </div>

  </xsl:template>

 

  <xsl:template match="act:eMail">

      <div class="Email">

        <a>

          <xsl:attribute name="href">

            <xsl:text>mailto:</xsl:text>

            <xsl:value-of select="act:eMailAddress" />

          </xsl:attribute>

          <xsl:value-of select="act:eMailAddress" />

        </a>

      </div>

  </xsl:template>

</xsl:stylesheet>

 

Grouping by Element Type

One thing that you can take advantage of in Visual Basic is the ability to group transforms by element type. For example, the ContactTypes.xsd file that we have been using in our AdventureWorks samples defines an address type, and e-mail type, and a phone number type. ContactTypes.xsd also identifies three different element names that are the address type and five different element names that are the phone number type. In XSLT, you would create a template for each address type and end up with three different templates for information that is, essentially, formatted the same. In Visual Basic, you can easily create a query for all different address or phone number elements and pass that to the same VB function, which acts as your XSLT template. For example:

    Dim addresses = (From addr In info...<act:homePostalAddress>).Union( _

                     From addr In info...<act:physicalDeliveryOfficeName>).Union( _

                     From addr In info...<act:registeredAddress>).ToList()

 

    For Each address In addresses

      TransformAddress(address)

    Next

 

    ...

 

    Private Sub TransformAddress(ByVal address As XElement)

      Dim addressHtml = _

        <div class="Address">

          <%= address.<act:Street>.Value %><br/>

          <%= address.<act:City>.Value & ", " %>&#32;

          <%= address.<act:StateProvince>.Value %>&#32;

          <%= address.<act:PostalCode>.Value %><br/>

          <%= address.<act:CountryRegion>.Value %><br/>

          <%= _

            GetSpecialInstructions(address.<act:SpecialInstructions>.ToList()) %>

        </div>

 

      address.ReplaceWith(addressHtml)

    End Sub

Leave a Comment
  • Please add 8 and 7 and type the answer here:
  • Post
  • PingBack from http://www.travel-hilarity.com/airline_travel/?p=4129

  • Recipe 3 showed one way to work with mixed XML content using the XML Descendant axis property and the

  • XSLT の ID 変換は、その名称が示すとおり、ある要素または属性の ID を新しい ID で置き換えます。 ID 変換は、構造があまり固定されていない XML マークアップを操作するときに特に重要です。ニュース記事の文書構造を考えてみましょう。タイトルが記事内で参照されると、そのタイトルは斜体で強調表示されます。ただし、その記事の

  • 「 レシピ 3 」では、混在 XML コンテンツを XML 子孫軸プロパティ と ReplaceWith メソッドを使用して操作する 1 つの方法を紹介しました。これは ID 変換の 1 つの方法であり、今後の投稿で別の方法を取り上げます。完全に機能する

Page 1 of 1 (4 items)