XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Automating Stylesheet Creation
by Bob DuCharme | Pages: 1, 2, 3

The generating stylesheet's first template rule creates a template rule in the generated stylesheet that will delete all elements named by name elements in the conversionSpec.xml file's delete element. With the version of conversionSpec.xml shown above, we want it to generate this:

<wh:template match="foobarNum|weather"/> 

The template rule that creates this uses the xsl:element element to create an element named "wh:template" and an xsl:attribute element to create a match attribute for wh:template. It creates the attribute's value by iterating through the delete element's name children and adding each name element's value and a pipe delimiter if another name is coming up.

<!-- Create template rule to delete elements. -->
<xsl:template match="delete">
  <xsl:element name="wh:template">
    <xsl:attribute name="match">
      <xsl:for-each select="name">
        <xsl:value-of select="."/>
        <xsl:if test="following-sibling::name">
          <xsl:text>|</xsl:text>
        </xsl:if>
      </xsl:for-each>
    </xsl:attribute>
    <!-- Created element is empty. -->
  </xsl:element>
</xsl:template>

Once the attribute is created, nothing else is necessary, because as the sample wh:template element above shows, we want to create an empty template rule that does nothing when it finds elements described by the match condition.

The basic pattern of this template rule works for other stylesheet tasks in which one template rule in the generated stylesheet handles multiple elements. The generating stylesheet's next template rule, which creates a template rule that will output the contents of any matched elements without their tags, resembles the one above, with the new part bolded.

 <!-- Create template rule to strip tags. -->
<xsl:template match="strip">
  <xsl:element name="wh:template">
    <xsl:attribute name="match">
      <xsl:for-each select="name">
        <xsl:value-of select="."/>
        <xsl:if test="following-sibling::name">
          <xsl:text>|</xsl:text>
        </xsl:if>
      </xsl:for-each>
    </xsl:attribute>
    <wh:apply-templates/>
  </xsl:element>
</xsl:template>

Once this template rule finishes the match condition listing the relevant elements, it adds a wh:apply-templates element to the result, creating an instruction for the generated stylesheet that looks like this (remember, conversionSpecs.xml only had one name child of its strip element):

<wh:template match="email">
    <wh:apply-templates/>
  </wh:template>

The two template rules we've seen so far from the generating stylesheet follow a common pattern that will be useful in any stylesheet that generates other stylesheets. The next three template rules follow another pattern that is handy when you need to generate a separate template rule for each element named in the conversion instructions as the object of a specific task.

The template rule below creates a template rule in the generated stylesheet for each reorder element in the conversion instructions. The generated template rule will name the element to reorder in its match condition, copy all the attributes verbatim, and then use a series of wh:apply-templates instructions to output the parent element's children in the order specified in the name children of the reorder element's children element:

<!-- Create a template rule for each element
     that needs to be reordered. -->
<xsl:template match="reorder">
  <wh:template match="{parent}">
    <wh:copy>
      <wh:apply-templates select="@*"/>
      <xsl:for-each select="children/name">
        <wh:apply-templates select="{.}"/>
      </xsl:for-each>
    </wh:copy>
  </wh:template>
</xsl:template>  

The second of the three template rules that creates a generated template for each element that needs its own match condition, finds the conversion instruction's wrap elements and uses its two child elements to create a generated template rule that will wrap the named element, when found, with an element named by the wrapper element:

<!-- Create a template rule for each element that
     needs to be wrapped in another element. -->
<xsl:template match="wrap">
  <wh:template match="{name}">
    <xsl:element name="{wrapper}">
      <wh:copy>
        <wh:apply-templates select="@*|node()"/>
      </wh:copy>
    </xsl:element>
  </wh:template>
</xsl:template> 

The last of these three template rules creates a template rule that will convert attributes to subelements when it finds an attribute2element element in the conversion instructions:

 <!-- Create a template rule for each attribute that
     gets converted to a subelement. -->
<xsl:template match="attribute2element">
  <wh:template match="{name}">
    <wh:element name="{elName}">
      <wh:value-of select="."/>
    </wh:element>
  </wh:template>
</xsl:template>

Pages: 1, 2, 3

Next Pagearrow