XML

Yet Another XSLT Example

I'm a little concerned about the fact that you've only seen how to create an XSLT style sheet that mimics the functionality of an existing CSS style sheet. The reason for the concern is because you might be wondering why you wouldn't just create the style sheet in CSS since the CSS version is obviously simpler to code. The answer is that you probably would be smarter to use CSS for the example style sheets that you've seen thus far because they really do nothing more than format XML content for display. The real power of XSLT is revealed when you must go a step further and actually manipulate and extract information from XML content. In this section you create an XSLT style sheet that conditionally displays content according to its value and that also performs an interesting calculation on information in a document.

The example document for this style sheet is a document that stores a list of vehicles for sale. If you've ever shopped for cars on the Internet, you are probably familiar with the process of searching through lists of cars according to certain criteria. In this example you learn how to use an XSLT style sheet to format vehicle information intelligently. The Vehicles example document is coded in a custom XML language that would be suitable for an online car shopping web site. Listing 12.3 contains the code for the vehicles.xml document.

Listing 12.3. The Vehicles Example XML Document
 1: <?xml version="1.0"?>
 2: <?xml-stylesheet type="text/xsl" href="vehicles.xsl"?>
 3:
 4: <vehicles>
 5:   <vehicle year="2004" make="Acura" model="3.2TL">
 6:     <mileage>13495</mileage>
 7:     <color>green</color>
 8:     <price>33900</price>
 9:     <carfax buyback="no" />
10:   </vehicle>
11:
12:   <vehicle year="2005" make="Acura" model="3.2TL">
13:     <mileage>07541</mileage>
14:     <color>white</color>
15:     <price>33900</price>
16:     <carfax buyback="yes" />
17:   </vehicle>
18:
19:   <vehicle year="2004" make="Acura" model="3.2TL">
20:     <mileage>18753</mileage>
21:     <color>white</color>
22:     <price>32900</price>
23:     <carfax buyback="yes" />
24:   </vehicle>
25:
26:   <vehicle year="2004" make="Acura" model="3.2TL">
27:     <mileage>28434</mileage>
28:     <color>black</color>
29:     <price>31995</price>
30:     <carfax buyback="yes" />
31:   </vehicle>
32:
33:   <vehicle year="2004" make="Acura" model="3.2TL">
34:     <mileage>22422</mileage>
35:     <color>silver</color>
36:     <price>31995</price>
37:     <carfax buyback="no" />
38:   </vehicle>
39:
40:   <vehicle year="2004" make="Acura" model="3.2TL">
41:     <mileage>18350</mileage>
42:     <color>silver</color>
43:     <price>32995</price>
44:     <carfax buyback="no" />
45:   </vehicle>
46:
47:   <vehicle year="2004" make="Acura" model="3.2TL">
48:     <mileage>12163</mileage>
49:     <color>gold</color>
50:     <price>31995</price>
51:     <carfax buyback="yes" />
52:   </vehicle>
53:
54:   <vehicle year="2004" make="Acura" model="3.2TL">
55:     <mileage>23182</mileage>
56:     <color>silver</color>
57:     <price>31995</price>
58:     <carfax buyback="no" />
59:   </vehicle>
60:
61:   <vehicle year="2003" make="Acura" model="3.2TL">
62:     <mileage>37775</mileage>
63:     <color>grey</color>
64:     <price>22995</price>
65:     <carfax buyback="yes" />
66:   </vehicle>
67:
68:   <vehicle year="2003" make="Acura" model="3.2TL">
69:     <mileage>34503</mileage>
70:     <color>black</color>
71:     <price>22995</price>
72:     <carfax buyback="yes" />
73:   </vehicle>
74:   <vehicle year="2003" make="Acura" model="3.2TL">
75:     <mileage>42670</mileage>
76:     <color>black</color>
77:     <price>23995</price>
78:     <carfax buyback="no" />
79:   </vehicle>
80:
81:   <vehicle year="2003" make="Acura" model="3.2TL">
82:     <mileage>48405</mileage>
83:     <color>gold</color>
84:     <price>22995</price>
85:     <carfax buyback="yes" />
86:   </vehicle>
87: </vehicles>

This example document represents XML data that you might receive as part of a search result for a specific type of car, in this case an Acura TL. The XML code shows how the Vehicles document relies on a relatively simple markup language consisting of only a few elements: vehicles, vehicle, mileage, color, price, and carfax. Each vehicle in the document is coded as a vehicle element within the parent vehicles element. In addition to the mileage, color, and price of each vehicle, which are coded as child elements, the year, make, and model of each vehicle are coded as attributes of the vehicle element. There is an additional child element called carfax that is used to code whether or not a vehicle is covered under the CARFAX Buyback Guarantee. This element includes an attribute named buyback that can be set to either yes or no.

The online automotive service CARFAX offers a search that you can perform to find out the history of vehicles. The CARFAX Buyback Guarantee helps to ensure that the information about a vehicle on CARFAX is accurate. If not, CARFAX will buy the car back from you. To learn more about CARFAX, visit them online at http://www.carfax.com.

So what exactly should an XSLT style sheet do with this document? For the purposes of this example, I'd first like to see the style sheet sort the vehicles according to a certain criterion, such as price. Following is a template that carries out this kind of sorting process using the xsl:sort element and its order attribute:

<xsl:template match="vehicles">
  <xsl:apply-templates select="vehicle">
    <xsl:sort select="@price" order="ascending"/>
  </xsl:apply-templates>
</xsl:template>

This template sorts vehicles according to price and in ascending order (cheapest to most expensive). Although price is certainly an important factor, not all buyers are driven solely by price. In fact, you may be determined to buy a car with low miles, and it could very well be that the CARFAX guarantee is important to you. Knowing this, I thought it might be interesting to highlight vehicles that are under a certain mileage (20k miles) and that have the CARFAX guarantee. This task can be carried out using xsl:when and xsl:otherwise elements, which allow you to conditionally transform XML content, in addition to sorting vehicles by price.

One final piece of information that would be interesting to know is the average price of the vehicles in the document; lucky for us, XSLT is quite capable of performing this calculation without much work. Following is an example of how this calculation could be carried out in a template:

<xsl:value-of select="round(sum(vehicles/vehicle/price) div
  count(vehicles/vehicle))"/>

This code makes use of the round(), sum(), and count() functions to carry out the average price calculation.

The complete XSLT style sheet for the Vehicles document is similar to the other XSLT style sheets you've seenit must transform the XML content into XHTML so that it can be viewed in a web browser. Unlike those style sheets, however, this one must be structured a little differently. First off, the root template has much more responsibility because there is a fair amount of formatting involved in listing the vehicles, because they need to be listed in a tabular format. Additionally, the template for the main vehicles element is kind of interesting because it must sort its child vehicle elements according to the price of each vehicle.

I think you know enough about the required functionality of the Vehicles style sheet to take a look at the complete code for it, which is shown in Listing 12.4.

Listing 12.4. The vehicles.xsl Style Sheet Used to Transform and Format the Vehicles XML Document
  1: <?xml version="1.0"?>
  2: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3:   <xsl:template match="/">
  4:     <html>
  5:       <head>
  6:         <title>Used Vehicles</title>
  7:       </head>
  8:
  9:       <body background="money.jpg">
 10:         <h1 style="background-color:#446600;
 11:           color:#FFFFFF; font-size:20pt; text-align:center;
 12:           letter-spacing: 12pt">Used Vehicles</h1>
 13:         <table align="center" border="2px">
 14:           <tr>
 15:             <th>Year</th>
 16:             <th>Make</th>
 17:             <th>Model</th>
 18:             <th>Mileage</th>
 19:             <th>Color</th>
 20:             <th>Price</th>
 21:             <th>CARFAX</th>
 22:           </tr>
 23:           <xsl:apply-templates/>
 24:           <tr style="font-weight:bold">
 25:             <td colspan="3"></td>
 26:             <td colspan="2">Average price:</td>
 27:             <td>
 28:               $<xsl:value-of select="round(sum(vehicles/vehicle/price) div
 29:               count(vehicles/vehicle))"/>
 30:             </td>
 31:           </tr>
 32:         </table>
 33:       </body>
 34:     </html>
 35:   </xsl:template>
 36:
 37:   <xsl:template match="vehicles">
 38:       <xsl:apply-templates select="vehicle">
 39:         <xsl:sort select="price" order="ascending"/>
 40:       </xsl:apply-templates>
 41:   </xsl:template>
 42:
 43:   <xsl:template match="vehicle">
 44:    <xsl:choose>
 45:      <xsl:when test="mileage &lt; 20000 and carfax/@buyback = 'yes'">
 46:        <tr style="color:#446600; font-weight:bold">
 47:          <td><xsl:value-of select="@year"/></td>
 48:          <td><xsl:value-of select="@make"/></td>
 49:          <td><xsl:value-of select="@model"/></td>
 50:          <td><xsl:value-of select="mileage"/></td>
 51:          <xsl:apply-templates select="color" />
 52:          <td>$<xsl:value-of select="price"/></td>
 53:           <xsl:apply-templates select="carfax" />
 54:         </tr>
 55:       </xsl:when>
 56:       <xsl:otherwise>
 57:         <tr>
 58:           <td><xsl:value-of select="@year"/></td>
 59:           <td><xsl:value-of select="@make"/></td>
 60:           <td><xsl:value-of select="@model"/></td>
 61:           <td><xsl:value-of select="mileage"/></td>
 62:           <xsl:apply-templates select="color" />
 63:           <td>$<xsl:value-of select="price"/></td>
 64:           <xsl:apply-templates select="carfax" />
 65:         </tr>
 66:       </xsl:otherwise>
 67:     </xsl:choose>
 68:   </xsl:template>
 69:
 70:   <xsl:template match="color">
 71:     <xsl:choose>
 72:       <xsl:when test=". = 'black'">
 73:         <td style="background: #000000"></td>
 74:       </xsl:when>
 75:       <xsl:when test=". = 'red'">
 76:         <td style="background: #880000"></td>
 77:       </xsl:when>
 78:       <xsl:when test=". = 'green'">
 79:         <td style="background: #008800"></td>
 80:       </xsl:when>
 81:       <xsl:when test=". = 'blue'">
 82:         <td style="background: #000088"></td>
 83:       </xsl:when>
 84:       <xsl:when test=". = 'silver'">
 85:         <td style="background: #CCCCCC"></td>
 86:       </xsl:when>
 87:       <xsl:when test=". = 'gray' or . = 'grey'">
 88:         <td style="background: #666666"></td>
 89:       </xsl:when>
 90:       <xsl:when test=". = 'gold'">
 91:         <td style="background: #CC9933"></td>
 92:       </xsl:when>
 93:       <xsl:otherwise>
 94:         <td style="background: #FFFFFF"></td>
 95:       </xsl:otherwise>
 96:     </xsl:choose>
 97:   </xsl:template>
 98:
 99:   <xsl:template match="carfax">
100:     <xsl:choose>
101:       <xsl:when test="@buyback = 'yes'">
102:         <td style="text-align: center"><img src="checkmark.gif"
103:           alt="CARFAX Buyback Guarantee" /></td>
104:       </xsl:when>
105:       <xsl:otherwise>
106:         <td></td>
107:       </xsl:otherwise>
108:     </xsl:choose>
109:   </xsl:template>
110: </xsl:stylesheet>

Although the code is a little long compared to the other style sheets you've seen, it does some pretty neat things with the vehicle data. First of all, the XHTML web page is set up and a table is created with a caption for the vehicle list; this all takes place in the first part of the root template (lines 422). On line 23 the xsl:apply-templates element is used to invoke the other templates in the style sheet, which results in the vehicle data getting transformed and formatted into XHTML table data. The root template then continues by calculating and displaying the average price of the vehicles on the last row of the table (lines 2431). Notice that the round(), sum(), and count() functions are all used in this calculation, along with the div operator.

There are four other templates defined in this style sheet: vehicles, vehicle, color, and carfax. The vehicles template invokes the vehicle template and also sorts the vehicle elements using the xsl:sort element (line 39). The sort is an ascending sort according to the price attribute of the vehicle elements.

The vehicle template is intriguing in that it uses the xsl:choose, xsl:when, and xsl:otherwise elements to set up two branches of code that are conditionally carried out based upon the values of the mileage element and the buyback attribute of the carfax element within each vehicle element. More specifically, vehicles with mileage less than 20,000 miles that have the CARFAX guarantee are highlighted (line 45). Notice on this line (45) that a fairly interesting expression is used to check for vehicles matching the mileage and CARFAX requirements. If there is a match with this expression, the details of the vehicle are displayed in a green, bold font (lines 4654); if there is no match, the vehicle details are displayed in a normal font, as indicated in the xsl:otherwise element (lines 5765).

The last two templates focus on the color and CARFAX information for each vehicle. Instead of just displaying the name of the color as text, a colored rectangle is drawn on the screen to help provide a better visual interface to the data. In other words, you see the actual color of the vehicle instead of the name of the color. The color template (lines 7097) is what makes this functionality possible.

The carfax template appears last, and is responsible for displaying an image of a checkmark that identifies whether a vehicle is covered by the CARFAX BuyBack Guarantee. The value of the buyback attribute is used in the conditional, and if its value is set to yes the image checkmark.gif is displayed.

You now have a pretty good idea regarding what the resulting web page should look like given the code in the vehicles.xsl style sheet. Figure 12.3 shows the vehicles.xml document as viewed in Internet Explorer using this style sheet.

Figure 12.3. The Vehicles example document is displayed in Internet Explorer using the vehicles. xsl style sheet.

The figure reveals how the XSLT style sheet manipulates the Vehicles XML document in several ways. First off, the vehicles are sorted according to increasing price, which allows you to quickly determine their different price points. Secondly, all the vehicles with less than 20,000 miles that have a CARFAX BuyBack Guarantee are highlighted with a bold font in a different color to make them stand out. Finally, the average price of the vehicles is calculated and displayed as the last item in the table, which would be useful information for a buyer trying to gauge how much vehicles cost.