Using Stylesheet Elements

XSL defines about twenty elements for transforming XML documents. So far, you have seen the <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 require 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.11 shows the simple.xsl stylesheet shown in Listing 17.2 enhanced to strip whitespace from all elements.

Listing 17.11. Full Text of simpleStrip.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
</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. So what is needed is a way of inserting whitespace into the output stream.

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.12.

Listing 17.12. Full Text of simpleSpace.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="*">
 <xsl:text>
</xsl:text>
 <xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

The blank line is inserted by the <xsl:text> element. 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 itself, which is why the indented <xsl:apply-templates/> tag does not insert any whitespace before the output text.

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 an XSLT 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 how to insert 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 lets 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:

<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 saw how to convert one XML document into 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 an EJB 2.1 Deployment Descriptor (DD) for two Session beans, as shown in Listing 17.13.

Listing 17.13. Full Text of dd.xml
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1"
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee
/ejb-jar_2_1.xsd">
 <display-name>Agency</display-name>
 <enterprise-beans>
  <session>
   <display-name>AgencyBean</display-name>
   <ejb-name>AgencyBean</ejb-name>
   <home>agency.AgencyHome</home>
   <remote>agency.Agency</remote>
   <ejb-class>agency.AgencyBean</ejb-class>
   <session-type>Stateless</session-type>
   <transaction-type>Bean</transaction-type>
  </session>
  <session>
   <display-name>AdvertiseBean</display-name>
   <ejb-name>AdvertiseBean</ejb-name>
   <home>agency.AdvertiseHome</home>
   <remote>agency.Advertise</remote>
   <ejb-class>agency.AdvertiseBean</ejb-class>
   <session-type>Stateful</session-type>
   <transaction-type>Bean</transaction-type>
  </session>
 </enterprise-beans>
</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 specified in 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.14. The output from applying this stylesheet is shown in Listing 17.15.

NOTE

Although you can view this example using the ApplyXSLT servlet, it might be easier to use the supplied command-line program called Transform. Run this program using asant Transform and supply the stylesheet name (JSP/examples/session.xsl) and XML filename (JSP/examples/dd.xml); the transformed document will be displayed on the screen.


Listing 17.14. Full Text of session.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="session">
 <xsl:element name="{./session-type}">
  <xsl:apply-templates/>
 </xsl:element>
</xsl:template>
<xsl:template match="session/session-type"/>
<xsl:template match="*|@*|comment()|processing-instruction()|text()">
 <xsl:copy>
  <xsl:apply-templates
              select="*|@*|comment()|processing-instruction()|text()"/>
 </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Listing 17.15. Applying session.xsl to dd.xml
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1"
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee
/ejb-jar_2_1.xsd">
 <display-name>Agency</display-name>
 <enterprise-beans>
  <Stateless>
   <display-name>AgencyBean</display-name>
   <ejb-name>AgencyBean</ejb-name>
   <home>agency.AgencyHome</home>
   <remote>agency.Agency</remote>
   <ejb-class>agency.AgencyBean</ejb-class>

   <transaction-type>Bean</transaction-type>
  </Stateless>
  <Stateful>
   <display-name>AdvertiseBean</display-name>
   <ejb-name>AdvertiseBean</ejb-name>
   <home>agency.AdvertiseHome</home>
   <remote>agency.Advertise</remote>
   <ejb-class>agency.AdvertiseBean</ejb-class>

   <transaction-type>Bean</transaction-type>
  </Stateful>
 </enterprise-beans>
</ejb-jar>

Attributes and Attribute Sets

For the example jobs.xml document in Listing 17.3 and the XSLT stylesheet table.xsl in Listing 17.9, 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 jobs.xml, the output heading for a job for winston would look like the following:

<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 still 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.16.

Listing 17.16. Full Text of tableStyle.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <HTML>
    <HEAD> <TITLE>Job Details</TITLE> </HEAD>
    <BODY>
      <xsl:apply-templates/>
      <P><A HREF='xsltForm.jsp'>Back to xsltForm</A></P>
    </BODY>
  </HTML>
</xsl:template>
<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>
 <P><xsl:apply-templates select="description"/></P>
 <TABLE border="1">
 <xsl:apply-templates select="skill|location"/>
 </TABLE>
</xsl:template>
<xsl:template match="description">
 <I><xsl:value-of select="."/></I>
</xsl:template>
<xsl:attribute-set name="rowStyle">
 <xsl:attribute name="face">Arial,sans-serif</xsl:attribute>
 <xsl:attribute name="color">blue</xsl:attribute>
</xsl:attribute-set>
<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>
</xsl:stylesheet>

Additional XSL Elements

XSL supports a number of elements that can provide program language-like capabilities within an XSLT stylesheet. Using these elements requires a good knowledge of the XPath notation. Because you have only seen some of the XPath notation today, 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 attaches numbers to job definitions:

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

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

Listing 17.17. Full Text of tableCount.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <HTML>
    <HEAD> <TITLE>Job Details</TITLE> </HEAD>
    <BODY>
      <xsl:apply-templates/>
      <P><A HREF='xsltForm.jsp'>Back to xsltForm</A></P>
    </BODY>
  </HTML>
</xsl:template>
<xsl:template match="job">
 <H2><xsl:number level="any"/>.
   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>
 <P><xsl:apply-templates select="description"/></P>
 <TABLE border="1">
 <xsl:apply-templates select="skill|location"/>
 </TABLE>
</xsl:template>
<xsl:template match="description">
 <I><xsl:value-of select="."/></I>
</xsl:template>
<xsl:attribute-set name="rowStyle">
 <xsl:attribute name="face">Arial,sans-serif</xsl:attribute>
 <xsl:attribute name="color">blue</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="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>
<xsl:template match="skill">
 <TR><TD><FONT xsl:use-attribute-sets="rowStyle">
   Skill <xsl:number/>:
 </FONT></TD><TD><FONT xsl:use-attribute-sets="rowStyle">
  <xsl:value-of select="."/>
 </FONT></TD></TR>
</xsl:template>
</xsl:stylesheet>

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


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.

NOTE

These tags work in the same way as the JSTL core actions with the same names.


The <xsl:if> and <xsl:when> elements use a test attribute that evaluates an XPath expression and includes the element body if the result of the test evaluates to 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> Sort elements in alphabetic or numeric order

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

  • <xsl:variable> Define variables that can be used in other XSL elements

  • <xsl:template> 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 (www.w3.org) 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.221.165.115