10.6 Transforming XML with XSLT

   

Sometimes XML documents are useful in and of themselves , but often those documents need to be transformed into some other format (for example, HTML or XML) that's a modification of the original document. Because transforming XML documents is commonplace, a language ”the XSL Transformation language (XSLT) ”has been developed explicitly for that purpose. XSLT's popularity is the impetus behind the JSTL <x:transform> action, which applies XSLT to an XML document.

A discussion of XSLT, which is a rather complex and powerful language, is beyond the scope of this book; however, the basic idea is that an XSLT stylesheet is applied to an XML document. That stylesheet transforms the XML document according to a set of XSLT template rules. How those rules are specified and how they work are discussed on page 444.

The <x:transform> action has three syntaxes; here's one of them: [11]

[11] Items in brackets are optional. See "<x:transform>" on page 553 for a complete description of <x:transform> syntax.

  <x:transform xml xslt [xmlSystemId] [xsltSystemId] [{var [scope]  result}]/>  

The xml attribute represents an XML document and, like the xml attribute for the <x:parse> action, can be a string or a reader. Additionally, the <x:transform> xml attribute can also be an instance of javax.xml.transform.Source or org.w3c.dom.Document or an object exported by <x:set> or <x:parse>. The xslt attribute, which represents an XSLT stylesheet, can be a string, reader, or an instance of javax.xml.transform.Source .

The xmlSystemId and xsltSystemId attributes specify URIs for resolving external entities for the XML document and XSLT stylesheet, respectively. See "Accessing External Entities" on page 460 for more information about accessing external entities.

By default, <x:transform> sends the transformed document to the current JspWriter . You can, however, capture that output in a scoped variable by specifying the var attribute (and, optionally , the scope attribute), or you can capture the output in an instance of javax.xml.transform.Result by specifying an object of that type for the result attribute.

You can also use the <x:transform> action with this syntax:

  <x:transform xml xslt [xmlSystemId] [xsltSystemId] [{var [scope]  result}]>   <x:param> actions   </x:transform>  

The preceding syntax lets you specify transformation parameters with <x:param> actions in the body of the <x:transform> action; see "Using Transformation Parameters" on page 446 for more information about using that syntax.

The third syntax lets you specify the XML document and, optionally, transformation parameters in the body of the <x:transform> action:

  <x:transform xslt [xmlSystemId] [xsltSystemId] [{var [scope]  result}]>   xml   optional <x:param> actions   </x:transform>  

Figure 10-4 shows a JSP page that uses <x:transform> to transform the Rolodex XML document listed in Listing 10.1 on page 424 to HTML.

Figure 10-4. Transforming XML with XSLT

graphics/10fig04.jpg

The JSP page shown in Figure 10-4 is listed in Listing 10.5.

The preceding JSP page is straightforward: the <c:import> action imports an XML document and an XSLT stylesheet. The resulting scoped variables , which reference string representations of the document and stylesheet, are specified for the <x:transform> action's xml and xslt attributes, respectively. Because neither the var attribute nor the result attribute is specified, the <x:transform> action sends its output to the current JspWriter .

As you can see from the preceding JSP page, the <x:transform> action is easy to use. Perhaps the most interesting aspect of that JSP page is the XSLT stylesheet it references, which is listed in Listing 10.6.

XSLT is a declarative language based on template rules. Each template rule consists of a pattern and an action and is specified with <xsl:template>; for example, in the preceding stylesheet there are three template rules:

 <xsl:template match='/'>...</xsl:template>  <xsl:template match='contact'>...</xsl:template> <xsl:template match='contact/*'>...</xsl:template> 
Listing 10.5 index.jsp (Transforming XML to HTML)
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>    <head>       <title>Performing XSLT Transformations</title>    </head>    <body>       <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %>       <%@ taglib uri='http://java.sun.com/jstl/xml'  prefix='x' %>       <%-- Import the XML file and XSLT stylesheet --%>       <c:import var='rolodex_xml' url='rolodex.xml'/>       <c:import var='rolodex_xsl' url='rolodex.xsl'/>       <%-- Perform the transformation --%>  <x:transform xml='${rolodex_xml}' xslt='${rolodex_xsl}'/>  </body> </html> 

The patterns for the rules are / , which matches the document's root element; contact , which matches contact elements; and contact/* , which matches all contact child elements.

The action for the rule matching the root element creates HTML that looks like this:

 <table border='1'>     <tr>       <th>First Name</th>       <th>Last Name</th>       <th>Company</th>       <th>Email</th>       <th>Work Phone</th>       <th>Home Phone</th>    </tr> </table> 

The rule matching the root element has an <xsl:apply-templates> tag after the table header for Home Phone . That tag recursively applies matching rules to the root element's children. Those rules create a table row for each contact element and table data for each contact child element.

The preceding XSLT stylesheet is relatively simple, but XSLT stylesheets can be quite complex and often require parameters. Specifying and using transformation parameters is the topic of the next section.

Listing 10.6 rolodex.xsl (Generating HTML)
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                   version="1.0">    <!-- Generate HTML for the document root -->  <xsl:template match='/'>  <table border='1'>          <tr>             <th>First Name</th>             <th>Last Name</th>             <th>Company</th>             <th>Email</th>             <th>Work Phone</th>             <th>Home Phone</th>          </tr>          <xsl:apply-templates/>       </table>    </xsl:template>    <!-- Create a table row for each item and apply templates -->  <xsl:template match='contact'>  <tr><xsl:apply-templates/></tr>    </xsl:template>    <!-- Create table data for each item and apply templates -->  <xsl:template match='contact/*'>  <td><xsl:apply-templates/></td>    </xsl:template> </xsl:stylesheet> 

Using Transformation Parameters

It's often necessary to parameterize XSLT stylesheets with transformation parameters; for example, the Web application shown in Figure 10-5 uses a transformation parameter to specify a table column that is removed from the HTML table shown in the top picture.

Figure 10-5. Using XSLT Parameters

graphics/10fig05.jpg

The Web application shown in Figure 10-5 consists of three JSP pages and an XSLT stylesheet. The top picture shows the welcome page, which performs an initial transformation of the XML document to an HTML table. The bottom two pictures in Figure 10-5 show a second JSP page that performs a transformation with a transformation parameter that specifies a column that's removed from the table. The middle picture shows the Email column removed, and the bottom picture shows the Home Phone column removed.

The JSP page shown in the top picture in Figure 10-5 is listed in Listing 10.7.

The preceding JSP page, which is the Web application's welcome page, imports the XML document and XSLT stylesheet and stores the resulting scoped variables in session scope. Subsequently, the JSP page performs an initial transformation with the <x:transform> action and includes a JSP page that creates the form that lets you select a column to remove. That JSP page ” form.jsp ”is listed in Listing 10.8.

The preceding JSP page creates a form with an HTML select element that retains its value when the JSP page is reloaded. [12] That form's action is transform.jsp , which is listed in Listing 10.9.

[12] See "Retaining Values for HTML Option Elements" on page  129 for more information about HTML select elements that retain their values.

Listing 10.7 index.jsp (Using Transformation Parameters)
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>    <head>       <title>Using Transformation Parameters</title>    </head>    <body>       <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %>       <%@ taglib uri='http://java.sun.com/jstl/xml'  prefix='x' %>       <%-- Import the XML file and XSLT stylesheet and store the            result in session-scoped variables, which are accessed            below and in transform.jsp --%>       <c:import var='rolodex_xml'  scope='session'  url='rolodex.xml'/>       <c:import var='rolodex_xsl'  scope='session'  url='rolodex.xsl'/>       <%-- Transform the XML document with the XSLT stylesheet --%>  <x:transform xml='${rolodex_xml}' xslt='${rolodex_xsl}'/>   <c:import url='form.jsp'/>  </body> </html> 

The preceding JSP page transforms the Rolodex XML document with the scoped variables representing the XML document and the XSLT stylesheet stored in session scope by the JSP page listed in Listing 10.7. The preceding JSP page specifies a transformation parameter with the <x:param> action. The name of that transformation parameter is filteredColumn , and its value is the value of the request parameter named filterThis , which is created by the JSP page listed in Listing 10.8. The XSLT stylesheet that's applied to the Rolodex XML document is rolodex.xsl , which is listed in Listing 10.10.

The preceding XSLT stylesheet first declares the filteredColumn transformation parameter. XSLT stylesheets must declare all transformation parameters that they use. If you do not declare transformation parameters in your stylesheets, they will not be available, even though you specify them with the <x:param> action, as is the case for the JSP page listed in Listing 10.9.

After declaring the filteredColumn transformation parameter, the preceding stylesheet specifies template rules for the document root element, the contact elements, the contact element's children, and phone elements. Each of those rules behaves differently, depending on the value of the filteredColumn transformation parameter. The rule that matches the document root element omits the table header for the appropriate column, depending on the filteredColumn transformation parameter; for example, if that parameter is email , the Email column is not generated.

Listing 10.8 form.jsp (Creating a Request Parameter)
 <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> <form action='transform.jsp'>    Remove a column:    <select name='  filterThis  '>       <option value='NONE'          <c:if test='${param.filterThis == "NONE"}'>             selected          </c:if> >NONE       </option>       <option value='email'          <c:if test='${param.filterThis == "email"}'>             selected          </c:if> >email       </option>       <option value='phone@home'          <c:if test='${param.filterThis == "phone@home"}'>             selected          </c:if> >home phone       </option>       <option value='phone@work'          <c:if test='${param.filterThis == "phone@work"}'>             selected          </c:if> >work phone       </option>    </select>    <p><input type='submit' value='Transform XML'/> </form> 

There are a couple of points of interest in the preceding stylesheet. First, that stylesheet uses the <xsl:if> tag, which, like the JSTL <x:if> action, evaluates its body content if the corresponding XPath expression evaluates to true . For example, the rule that matches contact child elements generates table data if the value of the filteredColumn transformation parameter does not match the local name of the child element. Second, even though the rule for contact child elements will match phone elements, separate rules are declared for those elements. Those rules are necessary because the filteredColumn transformation parameter specifies work phones with phone@work and home phones with phone@home , which do not match the local name of the phone elements, which is simply phone .

Listing 10.9 transform.jsp
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>    <head>       <title>Using Transformation Parameters</title>    </head>    <body>       <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %>       <%@ taglib uri='http://java.sun.com/jstl/xml'  prefix='x' %>       <%-- Transform the XML document with the XSLT stylesheet --%>       <x:transform xml='${rolodex_xml}' xslt='${rolodex_xsl}'>  <x:param name='filteredColumn'   value='${param.filterThis}'/>  </x:transform>       <c:import url='form.jsp'/>    </body> </html> 
Listing 10.10 rolodex.xsl (Declaring a Parameter)
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                   version="1.0">    <!-- Declare the filteredColumn parameter-->  <xsl:param name='filteredColumn'/>  <!-- Generate HTML for the document root -->  <xsl:template match='/'>  <table border='1'>          <tr>             <th>First Name</th>             <th>Last Name</th>             <th>Company</th>             <xsl:if test='$filteredColumn != "email"'>                <th>Email</th>             </xsl:if>             <xsl:if test='$filteredColumn != "phone@work"'>                <th>Work Phone</th>             </xsl:if>             <xsl:if test='$filteredColumn != "phone@home"'>                <th>Home Phone</th>             </xsl:if>             <xsl:apply-templates/>          </tr>       </table>    </xsl:template>    <!-- Create a table row for each item and apply templates -->  <xsl:template match='contact'>  <tr><xsl:apply-templates/></tr>    </xsl:template>    <!-- Create table data for all elements that are children         of contact elements, except for home and work phone, which         are processed below -->  <xsl:template match='contact/*'>  <xsl:if test='$filteredColumn != local-name()'>          <td><xsl:apply-templates/></td>       </xsl:if>    </xsl:template>    <!-- Create table data for the Work Phone element -->  <xsl:template match='contact/phone[@type="work"]'>  <xsl:if test='$filteredColumn != "phone@work"'>          <td><xsl:apply-templates/></td>       </xsl:if>    </xsl:template>    <!-- Create table data for the Home Phone element -->  <xsl:template match='contact/phone[@type="home"]'>  <xsl:if test='$filteredColumn != "phone@home"'>          <td><xsl:apply-templates/></td>       </xsl:if>    </xsl:template> </xsl:stylesheet> 

In effect, the stylesheet discussed in this section filters specified columns from the HTML table. Another approach is to filter the XML document itself when you parse the document. That approach is discussed in the next section.

   


Core JSTL[c] Mastering the JSP Standard Tag Library
Core JSTL[c] Mastering the JSP Standard Tag Library
ISBN: 131001531
EAN: N/A
Year: 2005
Pages: 124

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net