| [ directory ] |
|
8.5 Formatting XMLListing 8.5 does two separate but related things. It pulls out a chunk of an XML document, using the XPath expression //artist[1]/album, and then builds some HTML out of the values in the XML in the body of the x:forEach. This second part, translating XML into another format, is so common and so important that a whole new language?span class="docEmphasis">XSLT (Xtensible Stylesheet Language Transformations)梬as developed to make it easier. This language uses many of the ideas that have already been discussed. To begin, consider what would be needed in order to find every artist's name from a CD collection in an XML document and output the string "Albums for" followed by the name, enclosed in an H1 tag. This would pose no challenge: Simply specify the set of nodes to loop over with an x:forEach tag, using //artist as the set of items. Then, within the x:forEach tag, obtain the desired string, using <x:out select="@name"/>. XSLT takes these same concepts but replaces the idea of selecting a set of tags and then iterating them, substituting with the notion of patterns. Each clause of an XSLT file specifies a pattern to find in the XML file, such as all artists, all albums with a given name, or any other possibility XPath provides. A provided output template may include elements selected from the XML that matched the input. For example, the XSLT that will format artist names as desired is <xsl:template match="artist"> <h1>Albums by <xsl:value-of select="@name"/></h1> </xsl:template> This looks like the corresponding JSP code, with xsl:template playing the role of the x:forEach and xsl:value-of replacing the x:out. It is important to note the conceptual difference, however; xsl:template is not an iteration operator and does not perform an activity for every element of a set. Instead, it provides a rule saying that whenever and wherever the XPath expression given as match is found, the body will be processed. A similar clause could be added to put album names in level 2 headers: <xsl:template match="album"> <h2><xsl:value-of select="@name"/></h2> </xsl:template> However, one more thing must be done to make both of these clauses fit together. The rule given for artist specifies that a certain string should result and that no other actions should be taken. To get it to continue examining the rest of the document, XSLT must be told to do so, which can be done by adding the following after the string: <xsl:apply-templates select="album"/> This indicates that XSLT should continue processing the album elements within the artist. Order is important here; if xsl:apply-templates appeared before the string, the result would show first the albums and then the artist. Listing 8.6 rounds out the set of translations by putting track names in a bulleted list. Listing 8.6 The full XSLT file
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="artist">
<h1>Albums by <xsl:value-of select="@name"/></h1>
<xsl:apply-templates select="album"/>
</xsl:template>
<xsl:template match="album">
<h2><xsl:value-of select="@name"/></h2>
<ul>
<xsl:apply-templates select="track"/>
</ul>
</xsl:template>
<xsl:template match="track">
<li><xsl:value-of select="@name"/></li>
</xsl:template>
</xsl:stylesheet>
Note that the rule for album also needs an xsl:apply-templates in order to process the tracks. Once an XSLT file has been defined, using it from a JSP is almost ridiculously easy! Such a page is shown in Listing 8.7. Listing 8.7 Using XSLT from a JSP
<%@ taglib prefix="x"
uri="http://java.sun.com/jstl/xml" %>
<%@ taglib prefix="c"
uri="http://java.sun.com/jstl/core" %>
<c:import
url="http://localhost:8080/jspbook/chapter08/
collection.jsp"
var="xml"/>
<c:import
url="http://localhost:8080/jspbook/chapter08/style.xsl"
var="xslt"/>
<x:transform xslt="${xslt}" xml="${xml}"/>
In this example, the XML and XSLT files are loaded using c:import tags. Then the transformation is performed and the result displayed with the new x:transform tag. As the final outcome of the transformation is HTML, the content type need not be set, and a browser will be able to render it in the usual way, as shown in Figure 8.2. Figure 8.2. The result of an XSLT translation.
In a sense, this process has split the view layer into two smaller components. One, the XML, provides data from the model to the view. The second, the XSLT, contains all the presentation information. Using pure JSPs, these two actions are typically intertwined, with some bean tags getting data from the model and various iteration and conditional tags munging that data into the desired presentation. Both of these operations may legitimately be considered part of the view, and so having them in the same JSP is not a bad design. However, splitting them into separate components offers some new possibilities. It is often true that splitting pieces of a complex system into separate modules makes it easy to add new functionality. In this case, one new piece of functionality is the ability to change the apparence of a page easily without changing any of the underlying implementation. Listing 8.8 shows an alternative XSLT file that uses tables to format a CD collection instead of itemized lists. Listing 8.8 An alternative style
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="album">
<table border="1">
<tr>
<td><xsl:value-of select="parent::artist/@name"/></td>
<td><xsl:value-of select="@name"/></td>
</tr>
<xsl:apply-templates select="track"/>
</table><p></p>
</xsl:template>
<xsl:template match="track">
<tr><td colspan="2"><xsl:value-of
select="@name"/></td></tr>
</xsl:template>
</xsl:stylesheet>
This XSLT file does nothing for artist nodes. When it encounters an album, it creates a new table, the first row of which will have a column for the artist name and another for the album name. The artist name is obtained with a new kind of XSLT expression: parent::artist/@name. The parent:: portion indicates that the value should be obtained from the parent node, that is, the node that contains the current one. Because album nodes are contained within artist nodes, this will get the artist; from there, getting the name is done as usual. Now that a second style has been defined, Listing 8.7 can be easily modified to switch between them, based on user preference, as shown in Listing 8.9. Listing 8.9 Allowing the user to choose a style
<%@ taglib prefix="x"
uri="http://java.sun.com/jstl/xml" %>
<%@ taglib prefix="c"
uri="http://java.sun.com/jstl/core" %>
<c:import
url="http://localhost:8080/jspbook/chapter08/
collection.jsp"
var="xml"/>
<c:choose>
<c:when test="${param.style == 'list'}">
<c:import
url="http://localhost:8080/jspbook/chapter08/style.xsl"
var="xslt"/>
</c:when>
<c:otherwise>
<c:import
url="http://localhost:8080/jspbook/chapter08/
tablestyle.xsl"
var="xslt"/>
</c:otherwise>
</c:choose>
<x:transform xslt="${xslt}" xml="${xml}"/>
This example simply uses a c:choose tag to load one of two XSLT files into the xslt variable, which will then be used by the x:transform tag. The result of formatting with the table-based XSLT file is shown in Figure 8.3. Figure 8.3. An alternative XSLT translation.
|
| [ directory ] |
|