Using Stylesheet Elements

XSL defines about twenty elements for transforming XML documents. So far, you have seen the basic <xsl:template> element for defining template rules and the <xsl:apply-templates> and <xsl:value-of> rules for including data in the output document.

Many transformations can be defined just using these three elements. However, some of the more complex requirements need additional support from XSL.

Processing Whitespace and Text

By default, an XSL transformation retains the whitespace in the original document. This may not be required for the following reasons:

  • The whitespace is generally ignored when processing the output.

  • Users browsing the transformed document may find the whitespace misleading or annoying.

  • Some output document formats may be whitespace sensitive, so the transformation must control the way whitespace is written to the output.

The <xsl:strip-space> tag can be used to strip leading and trailing whitespace from an element's body. The tag takes an elements attribute that defines from which elements to strip the whitespace. Elements are selected using the XPath notation.

Listing 17.12 shows the simple.xsl stylesheet shown in Listing 17.2 enhanced to strip whitespace from all elements.

Listing 17.12. Full Text of simpleStrip.xsl
1: <?xml version="1.0"?>
2: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3: <xsl:strip-space elements="*"/>
4: </xsl:stylesheet>

Applying this stylesheet to the jobs.xml document produces the following output:

LondonMust like to talk and smokeCigar makerCriticWashingtonMust be honestTree surgeon

No whitespace has been included but, as you can see, this is not particularly readable because the whitespace between the elements has been lost. You could selectively strip whitespace from elements using tags, as shown in the following:

<xsl:strip-space elements="jobSummary|job"/>

But this will still retain multiple spaces in the non-stripped elements.

Inserting whitespace into the output stream is best done using the <xsl:text> element. Any whitespace inside the <xsl:text> element is retained. You could rewrite the default rule for all elements to include a single blank line before each element, as shown in Listing 17.13.

Listing 17.13. Full Text of simpleSpace.xsl
1: <?xml version="1.0"?>
2: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3: <xsl:strip-space elements="*"/>
4: <xsl:template match="*">
5:   <xsl:text>
6: </xsl:text>
7:   <xsl:apply-templates/>
8: </xsl:template>
9: </xsl:stylesheet>

The blank line is inserted at lines 5 and 6. Notice how the closing </xsl:text> tag is not indented; otherwise, additional whitespace would have been included. You could not use an empty tag here (<xsl:text/>) because you need to insert the end of line into the output document). The output from applying this stylesheet to jobs.xml is as follows:

<?xml version="1.0" encoding="UTF-8"?>



London
Must like to talk and smoke
Cigar maker
Critic

Washington
Must be honest
Tree surgeon

Whitespace is automatically stripped from the stylesheet, which is why the indented <xsl:apply-templates/> tag does not insert any whitespace before the output text.

The <xsl:text> tag is also used to insert text containing XML special characters (such as < and &) into the output stream. These characters are defined in the stylesheet using their character reference form (such as &lt;) and are output in the same format. Wrapping the symbols inside an <xsl:text> element with the disable-output- escaping attribute set to yes will output any special symbols as simple Unicode characters. The <xsl:text> element is most often used when the output document is not an XML document, or the output is not well formed XML.

Caution

Disabling the output escaping mechanism is dangerous and should only be used when no alternative solution can be found. The disable-output-escaping attribute is often abused, much like the goto statement in some programming languages.


The following example shows one way of inserting a piece of JavaScript into your Web page (using comments, as described in the “Adding Comments” section later in this chapter, is a better approach):

<xsl:template match="SCRIPT">
  <SCRIPT language="javascript">
    <xsl:text disable-output-escaping="yes">
      &lt;!--
        if (n &lt; 0 &amp;&amp;  m &gt; 0) {
          n = m;
        } // --&gt;
    </xsl:text>
  </SCRIPT>
</xsl:template>

This transforms to

<SCRIPT language="javascript">
<!--
  if (n < 0 && m > 0) {
    n = m;
  } // -->
</SCRIPT>

Adding Comments

You can add a comment to a stylesheet as follows:

<xsl:template match="job">
  <!--this is a job definition -->
  <xsl:apply-templates/>
</xsl:template>

Using XML comments in this way is treated as a stylesheet comment and is not inserted into the output stream. To include a comment in the output document, you must use the <xsl:comment> element, as shown in the following:

<xsl:template match="job">
  <xsl:comment>this is a job definition</xsl:comment>
  <xsl:apply-templates/>
</xsl:template>

The following example shows a better solution to inserting JavaScript in an HTML page:

<xsl:template match="SCRIPT">
  <SCRIPT language="javascript">
    <xsl:text>
      <xsl:comment>
        if (n &lt; 0 &amp;&amp;  m &gt; 0) {
          n = m;
        } // </xsl:comment>
    </xsl:text>
  </SCRIPT>
</xsl:template>

Another advantage of using <xsl:comment> is that the actual comment can be derived from data in the source XML document. The following example inserts the job customer and reference attributes into a comment:

<xsl:template match="job">
  <xsl:comment>Job definition for <xsl:value-of select="@customer"/>/ <xsl:value-of
 select="@reference"/></xsl:comment>
  <xsl:apply-templates/>
</xsl:template>

Finally, if you want to copy the comments from the original XML document into the transformed document, add the following rule to your stylesheet:

<xsl:template match="comment()">
  <xsl:comment><xsl:value-of select="."/></xsl:comment>
</xsl:template>

Attribute Values

Some transformations require output element attributes to vary according to the element being processed. As a simple example, think back to Day 16 when you first looked at XML and the simple structure for the job summary, as shown in the following:

<?xml version="1.0"?>
<jobSummary>
  <job>
    <customer>winston</customer>
    <reference>Cigar Trimmer</reference>
    <location>London</location>
    <description>Must like to talk and smoke</description>
    <!-- skills list for winston -->
    <skill>Cigar maker</skill>
    <skill>Critic</skill>
  </job>
</jobSummary>

This version didn't use attributes to represent the primary key of the job. Imagine that you have to convert this form of document into the new form using attributes. Your first attempt to do this might be as follows:

<xsl:template match="job">
  <job
    customer="<xsl:value-of select='./customer'/>"
    job="<xsl:value-of select='./reference'/>"
  />
  <xsl:apply-templates/>
</xsl:template>

Unfortunately, this won't work, because XML does not allow you to define elements inside attributes of other elements. You can always insert a < symbol inside an attribute value using &lt; but this would not be interpreted as an element.

To get around the XML restriction of not allowing elements to be defined inside attributes, XSLT stylesheets let you insert the value of elements inside attributes by enclosing the XPath name in braces, as shown in the following:

<xsl:template match="job">
  <job customer="{./customer}" job="{./reference}" />
  <xsl:apply-templates select='location|description|skill'/>
</xsl:template>

To prevent the nested <customer> and <reference> elements from being output by the default template rules, the <apply-templates> element selects the required child elements of the job element. An alternative syntax that excludes the unwanted elements and doesn't need you to explicitly list the required elements uses an XPATH expression notation that has not been discussed today. For completeness, this rule it is as follows:

<xsl:apply-templates select="./*[name()!='customer' and name()!='reference']"/>

If you want to convert the new style job element (with attributes) back to the one with nested elements, you use the following rule:

<xsl:template match="job">
  <job>
    <customer><xsl:value-of select="@customer/></customer>
    <reference><xsl:value-of select="@reference/></reference>
    <xsl:apply-templates/>
  </job>
</xsl:template>

Creating and Copying Elements

In the previous section, you learned how to convert one XML document to another by converting nested elements into tags. But what do you do if you want to convert an attribute into an element where the attribute value is the name of the element, or vice versa?

As a simple example, consider a Deployment Descriptor (DD) for two Session beans, as shown in Listing 17.14.

Listing 17.14. Full Text of dd.xml
 1: <?xml version="1.0" encoding="UTF-8"?>
 2: <!-- DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0/
/EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd' -->
 3: <ejb-jar>
 4:   <display-name>Agency</display-name>
 5:   <enterprise-beans>
 6:     <session>
 7:       <display-name>AgencyBean</display-name>
 8:       <ejb-name>AgencyBean</ejb-name>
 9:       <home>agency.AgencyHome</home>
10:       <remote>agency.Agency</remote>
11:       <ejb-class>agency.AgencyBean</ejb-class>
12:       <session-type>Stateless</session-type>
13:       <transaction-type>Bean</transaction-type>
14:     </session>
15:     <session>
16:       <display-name>AdvertiseBean</display-name>
17:       <ejb-name>AdvertiseBean</ejb-name>
18:       <home>agency.AdvertiseHome</home>
19:       <remote>agency.Advertise</remote>
20:       <ejb-class>agency.AdvertiseBean</ejb-class>
21:       <session-type>Stateful</session-type>
22:       <transaction-type>Bean</transaction-type>
23:     </session>
24:   </enterprise-beans>
25: </ejb-jar>

Imagine that a different application (or a future version of J2EE) decided that stateless and stateful Session beans were sufficiently different to warrant using different elements. For example,

<Stateless>
  <ejb-name>AgencyBean</ejb-name>
  ...
</Stateless>
<Stateful>
  <ejb-name>AdvertiseBean</ejb-name>
  ...
</Stateful>

To make this transformation, you need a rule that can generate an element whose name is derived from the original XML document. The XSL element that does this is called <xsl:element>, and the transformation shown previously is achieved by the following rule:

<xsl:template match="session">
  <xsl:element name="{./session-type}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>
<xsl:template match="session/session-type"/>

The name attribute to the <xsl:element> is the name of the element to define. In this example, the name is the value of the session-type child element (remember that braces take the value of a node when defining attributes).

The second rule in the previous example (<xsl:element name="{./session-type}">) ensures that the session-type child element is not included in the output document.

Sadly, there is one major problem with the previous example. All other elements are output using their text values, the element start and end tags and attributes have been lost from the document. The problem can be overcome using the <xsl:copy> element.

The <xsl:copy> element is used to copy elements from the XML source to the output document. The following rule is an identity transformation rule (the document is copied without any changes):

<xsl:template  match="*|@*|comment()|processing-instruction()|text()">
  <xsl:copy>
    <xsl:apply-templates select="*|@*|comment()|processing-instruction()|text()"/>
  </xsl:copy>
</xsl:template>

The template matches all elements and uses the <xsl:copy> element to copy the matched element. The body of the <xsl:copy> element must apply the template rules to the body of the matched XML node; otherwise, no output will occur.

The full stylesheet for transforming the old style DD into the new style is shown in Listing 17.15. The output from applying this stylesheet is shown in Listing 17.16.

Listing 17.15. Full Text of session.xsl
 1: <?xml version="1.0"?>
 2: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 3: <xsl:template match="session">
 4:   <xsl:element name="{./session-type}">
 5:     <xsl:apply-templates/>
 6:   </xsl:element>
 7: </xsl:template>
 8: <xsl:template match="session/session-type"/>
 9: <xsl:template  match="*|@*|comment()|processing-struction()|text()">
10:   <xsl:copy>
11:     <xsl:apply-templates select="*|@*|comment()|processing-instruction()|text()"/>
12:   </xsl:copy>
13: </xsl:template>
14: </xsl:stylesheet>

Listing 17.16. Applying session.xsl to dd.xml
 1: >java org.apache.xalan.xslt.Process -in XML/dd.xml -xsl XSL/session.xsl
 2: <?xml version="1.0" encoding="UTF-8"?>
 3: <!-- DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0/
/EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd' -->
 4: <ejb-jar>
 5:   <display-name>Agency</display-name>
 6:   <enterprise-beans>
 7:     <Stateless>
 8:       <display-name>AgencyBean</display-name>
 9:       <ejb-name>AgencyBean</ejb-name>
10:       <home>agency.AgencyHome</home>
11:       <remote>agency.Agency</remote>
12:       <ejb-class>agency.AgencyBean</ejb-class>
13:
14:       <transaction-type>Bean</transaction-type>
15:     </Stateless>
16:     <Stateful>
17:       <display-name>AdvertiseBean</display-name>
18:       <ejb-name>AdvertiseBean</ejb-name>
19:       <home>agency.AdvertiseHome</home>
20:       <remote>agency.Advertise</remote>
21:       <ejb-class>agency.AdvertiseBean</ejb-class>
22:
23:       <transaction-type>Bean</transaction-type>
24:     </Stateful>
25:   </enterprise-beans>
26: </ejb-jar>

Attributes and Attribute Sets

In the example jobSummary document in Listing 17.3 and the stylesheet table.xsl in Listing 17.10, you might like to be able to use a hyperlink to link the customer name to a URL of the form

/agency/advertise?customer=<customer_name>
						

Using the sample data from Listing 17.3, the output heading for a job for winston would look as follows:

<H2>Job ref: <A HREF="/agency/advertise?winston">winston</A>/Cigar Trimmer</H2>

Here, the value of the new attribute is derived from the XML source document. This can be achieved by using the <xsl:attribute> element, as shown in the following:

<xsl:template match="job">
  <H2>Job ref:
    <A>
      <xsl:attribute name="HREF">
        /agency/advertise?<xsl:value-of select="@customer"/>
      </xsl:attribute>
      <xsl:value-of select="@customer"/>
    </A>/<xsl:value-of select="@reference"/></H2>
  <TABLE border="1">
  <xsl:apply-templates/>
  </TABLE>
</xsl:template>

The <xsl:attribute> element defines an attribute for the enclosing element (in this case, <A>). More than one attribute can be defined, but all attributes must be defined before any other text or child nodes in the element.

Sometimes, the same attributes must be defined for many different tags. This is a common requirement when applying styles to HTML tables. Consider making every cell in the table contain text with a blue sans-serif font. You could define the style separately for every cell in the table, but this is hard to maintain should the style requirements change. Alternatively, you could use Cascading Style Sheets (CSS), but the most popular browsers do not support CSS in a consistent manner. An XSL solution is to use an attribute set.

The <xsl:attribute-set> element defines the attributes you can apply to multiple elements. An <xsl:attribute-set> defining a blue sans-serif font is as follows:

<xsl:attribute-set name="rowStyle">
  <xsl:attribute name="face">Arial,sans-serif</xsl:attribute>
  <xsl:attribute name="color">blue</xsl:attribute>
</xsl:attribute-set>

An attribute set is applied by using the xsl:use-attribute-sets attribute with an element. For example

<xsl:template match="skill|location">
  <TR><TD><FONT xsl:use-attribute-sets="rowStyle">
      <xsl:value-of select="name()"/>
  </FONT></TD><TD><FONT xsl:use-attribute-sets="rowStyle">
    <xsl:value-of select="."/>
  </FONT></TD></TR>
</xsl:template>

The transformed table definition for the job skill and location elements is as follows:

<TABLE border="1">
  <TR>
    <TD><FONT face="Arial,sans-serif" color="blue">location</FONT></TD>
    <TD><FONT face="Arial,sans-serif" color="blue">London</FONT></TD>
  </TR>
  <TR>
    <TD><FONT face="Arial,sans-serif" color="blue">skill</FONT></TD>
    <TD><FONT face="Arial,sans-serif" color="blue">Cigar maker</FONT></TD>
  </TR>
  <TR>
    <TD><FONT face="Arial,sans-serif" color="blue">skill</FONT></TD>
    <TD><FONT face="Arial,sans-serif" color="blue">Critic</FONT></TD>
  </TR>
</TABLE>

The complete stylesheet for transforming the jobSummary document into HTML is shown in Listing 17.17.

Listing 17.17. Full Text of tableStyle.xsl
 1: <?xml version="1.0"?>
 2: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 3: <xsl:template match="job">
 4:   <H2>Job ref:
 5:       <A>
 6:       <xsl:attribute name="HREF">
 7:         /agency/advertise?<xsl:value-of select="@customer"/>
 8:       </xsl:attribute>
 9:       <xsl:value-of select="@customer"/>
10:     </A>/<xsl:value-of select="@reference"/>
11:   </H2>
12:   <P><xsl:apply-templates select="description"/></P>
13:   <TABLE border="1">
14:   <xsl:apply-templates select="skill|location"/>
15:   </TABLE>
16: </xsl:template>
17: <xsl:template match="description">
18:   <I><xsl:value-of select="."/></I>
19: </xsl:template>
20: <xsl:attribute-set name="rowStyle">
21:   <xsl:attribute name="face">Arial,sans-serif</xsl:attribute>
22:   <xsl:attribute name="color">blue</xsl:attribute>
23: </xsl:attribute-set>
24: <xsl:template match="skill|location">
25:   <TR><TD><FONT xsl:use-attribute-sets="rowStyle">
26:       <xsl:value-of select="name()"/>
27:   </FONT></TD><TD><FONT xsl:use-attribute-sets="rowStyle">
28:     <xsl:value-of select="."/>
29:   </FONT></TD></TR>
30: </xsl:template>
31: </xsl:stylesheet>

Additional XSL Elements

XSL supports a number of elements that can provide program language like capabilities within a stylesheet. Using these elements requires a good knowledge of the XPath notation. Because you have only seen some of the XPath notation, you will only look very briefly at the elements that support programming capabilities.

Numbering Elements

A common requirement for many transformations is to insert numeric data into the output document, often to produce numbered lists.

The <xsl:number> element is used to insert numbers into the document. By default (that is, without attributes), this element inserts a count of the position of a context node in a list of elements of the same type.

To number the skills defined by a particular job, you could use the following rule:

<xsl:template match="skill">
  <TR><TD>
      Skill <xsl:number/>:
  </TD><TD>
    <xsl:value-of select="."/>
  </TD></TR>
</xsl:template>

Attributes can be supplied to the <xsl:number> element to determine what numeric value is inserted. The level="any" attribute is used to count all occurrences of the same type of node, regardless of where the node occurs in the document tree structure. The following example adds numbers to job definitions:

<xsl:template match="job">
  <H2><xsl:number level="any"/>.
      Job ref:
  ...
</xsl:template>

Listing 17.18 shows the final stylesheet for transforming the job.xml document using numbers to count jobs and skills within a job. The resulting Web page is shown in Figure 17.3.

Figure 17.3. Applying tableCount.xsl to job.xml.


Listing 17.18. Full Text of tableCount.xsl
 1: <?xml version="1.0"?>
 2: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 3: <xsl:template match="job">
 4:   <H2><xsl:number level="any"/>.
 5:       Job ref:
 6:       <A>
 7:       <xsl:attribute name="HREF">
 8:         /agency/advertise?<xsl:value-of select="@customer"/>
 9:       </xsl:attribute>
10:       <xsl:value-of select="@customer"/>
11:     </A>/<xsl:value-of select="@reference"/>
12:   </H2>
13:   <P><xsl:apply-templates select="description"/></P>
14:   <TABLE border="1">
15:   <xsl:apply-templates select="skill|location"/>
16:   </TABLE>
17: </xsl:template>
18: <xsl:template match="description">
19:   <I><xsl:value-of select="."/></I>
20: </xsl:template>
21: <xsl:attribute-set name="rowStyle">
22:   <xsl:attribute name="face">Arial,sans-serif</xsl:attribute>
23:   <xsl:attribute name="color">blue</xsl:attribute>
24: </xsl:attribute-set>
25: <xsl:template match="location">
26:   <TR><TD><FONT xsl:use-attribute-sets="rowStyle">
27:       <xsl:value-of select="name()"/>
28:   </FONT></TD><TD><FONT xsl:use-attribute-sets="rowStyle">
29:     <xsl:value-of select="."/>
30:   </FONT></TD></TR>
31: </xsl:template>
32: <xsl:template match="skill">
33:   <TR><TD><FONT xsl:use-attribute-sets="rowStyle">
34:       Skill <xsl:number/>:
35:   </FONT></TD><TD><FONT xsl:use-attribute-sets="rowStyle">
36:     <xsl:value-of select="."/>
37:   </FONT></TD></TR>
38: </xsl:template>
39: </xsl:stylesheet>

The <xsl:number> tag can also be used to

  • Insert numeric data obtained from an element or attribute in the XML source document

  • Count occurrences of a node

  • Count nodes from a given start point

  • Use letters or roman numerals instead of decimal integers or insert leading zeroes to make fixed-width numbers

Other Features

XSLT supports the optional inclusion of text in the output document using the following tags:

  • <xsl:if> The body of this element is only included if the test defined as an attribute is true.

  • <xsl:choose> Defines a list of choices, only one of which will be included.

  • <xsl:when> Defines a choice for an <xsl:choose> element.

  • <xsl:otherwise> Defines the default choice for an <xsl:choose> element if no <xsl:when>

    element is matched.

The <xsl:if> and <xsl:when> elements use a test attribute that evaluates an XPath expression and includes the element body if the test is true.

The following example tests if a Session bean from a DD is a stateless bean:

<xsl:if test="./session-type='Stateless'">
  ...
</xsl:if>

The following <xsl:choose> example selects different transformations for stateful and stateless Session beans:

<xsl:choose>
  <xsl:when test="./session-type='Stateless'">
    ...
  </xsl:when>
  <xsl:when test="./session-type='Stateful'">
    ...
  </xsl:when>
  <xsl:otherwise>
    ...
  </xsl:when>
</xsl:choose>

Other XSL elements include the following:

  • <xsl:sort> Used to sort elements by alphabetic or numeric order

  • <xsl:include> and <xsl:import> Import rules from other stylesheets

  • <xsl:variable> Used to define variables that can be used in other XSL elements

  • <xsl:template> Used to define templates that can be inserted in different parts of the transformed output (parameters can be used to customize each instance of a template)

As you can see, XSLT provides a powerful transformation language that really is too large and complex to cover in one lesson. Today's work on XSLT has been designed to give you an overview of what XSLT can do. For more information on XSLT, refer to the specifications on the W3C Web site or read the book Sams Teach Yourself XML in 21 Days from Sams Publishing.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.213.238