XML creation: Part 8

In this guide, I'll be creating an XML file to store the Playstation games I own, and ultimately make the XML file 'pretty' for Web browsers. I've done this in the past, with my vehicle gas XML document.

In part eight I'll be expanding upon our XSLT to group games based upon the system they belong to, as well as modify our XSLT to use xsl:appy-templates.

This time ...

I won't be going into a recap of what I've done in all seven of the previous parts. Check part 7 for that.

For part eight, I've modified the XML document by adding games for the Playstation 1 and 2.

While our XSLT will handle these, it ends up sorting the games altogether, by alphabetical title. This is well enough, however, what I'd like to do is separate these out so that the Playstation 1, 2, and 3 games are listed alphabetically by system.

Grouping by sorting

The easiest way to do this is to add the following two sorts before the existing sort. So we'll take the following line:

<xsl:sort select="title" data-type="text" />

We'll change this to the following (new lines in bold):

<xsl:sort select="system/console" data-type="text" />
<xsl:sort select="system/version" data-type="number" />

<xsl:sort select="title" data-type="text" />

xsl:apply-templates

As you may realize, we're using xsl:for-each to loop through our XML elements, and output the values. However, there is another way to do this.

Instead of using xsl:for-each we can use xsl:apply-templates.

For instance, we're listing the date and price in two different places. However, we're formatting this the same way.

First, we'll add the following, immediately before the closing of xsl:stylesheet.

<xsl:template match="price">
   for $<xsl:value-of select="." />
</xsl:template>

To some extent, we've seen this before, for our main template, with the exception of using game instead of price.

Next, we'll change the two price lines to the following, accordingly.

<xsl:apply-templates select="purchase/price" />

<xsl:apply-templates select="sell/price" />

After refreshing the XML file, you shouldn't notice any changes. However, if you make a modification to the xsl:template for price, you'll notice that the changes are made to the two price outputs.

If we were doing more with the date, we could do the same for these two elements.

You can see the XML file for this, as well as the XSLT file.

In that case ... another way of sorting

Since we can use xsl:apply-templates, how about using this in place of our xsl:for-each?

We begin with our current XSLT.


<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
 <xsl:template match="games">
  <html>
  <head>
   <title>James Skemp's video games</title>
  </head>
  <body>
  <h1>James Skemp's video game collection</h1>
  <p>The following is a list of video games owned by James Skemp, of <a href="http://strivinglife.com/">StrivingLife</a>.</p>
  <div style="border:1px dashed #999;margin:.25em;padding:1em;">
  <xsl:for-each select="game">
   <xsl:sort select="system/console" data-type="text" />
   <xsl:sort select="system/version" data-type="number" />
   <xsl:sort select="title" data-type="text" />
   <div style="margin:1em 0;">
    <span style="font-size:120%;font-weight:bold;"><xsl:value-of select="title" /></span>
    - <xsl:value-of select="system/console" />
    &#160;<xsl:value-of select="system/version" />
    <div style="margin-left:1em;">
     Bought <xsl:value-of select="purchase/date" />
     <xsl:apply-templates select="purchase/price" />
     at <xsl:value-of select="purchase/place" />
    </div>
    <xsl:if test="own = 'no'">
     <div style="margin-left:1em;">
      Sold <xsl:value-of select="sell/date" />
      <xsl:apply-templates select="sell/price" />
     </div>
    </xsl:if>
    <div style="margin-left:1em;">
     Still own? <xsl:value-of select="own" />
    </div>
    <xsl:if test="notes != ''">
     <div style="font-style:italic;margin-left:1em;">
      Notes: <xsl:value-of select="notes" />
     </div>
    </xsl:if>
   </div>
  </xsl:for-each>
  </div>
  </body>
  </html>
 </xsl:template>
 <xsl:template match="price">
  for $<xsl:value-of select="." />
 </xsl:template>
</xsl:stylesheet> 

After some modifications, we'll end up with the following XSLT, which also puts games for newer versions of consoles at the top.


<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
 <xsl:template match="games">
  <html>
  <head>
   <title>James Skemp's video games</title>
  </head>
  <body>
  <h1>James Skemp's video game collection</h1>
  <p>The following is a list of video games owned by James Skemp, of <a href="http://strivinglife.com/">StrivingLife</a>.</p>
  <div style="border:1px dashed #999;margin:.25em;padding:1em;">
   <xsl:apply-templates select="game">
    <xsl:sort select="system/console" data-type="text" />
    <xsl:sort select="system/version" data-type="number" order="descending" />
    <xsl:sort select="title" data-type="text" />
   </xsl:apply-templates>
  </div>
  </body>
  </html>
 </xsl:template>
 <xsl:template match="game">
  <div style="margin:1em 0;">
   <span style="font-size:120%;font-weight:bold;"><xsl:value-of select="title" /></span>
   - <xsl:value-of select="concat(system/console, ' ', system/version)" />
   <div style="margin-left:1em;">
    Bought <xsl:value-of select="purchase/date" />
    <xsl:apply-templates select="purchase/price" />
    at <xsl:value-of select="purchase/place" />
   </div>
   <xsl:if test="own = 'no'">
    <div style="margin-left:1em;">
     Sold <xsl:value-of select="sell/date" />
     <xsl:apply-templates select="sell/price" />
    </div>
   </xsl:if>
   <div style="margin-left:1em;">
    Still own? <xsl:value-of select="own" />
   </div>
   <xsl:if test="notes != ''">
    <div style="font-style:italic;margin-left:1em;">
     Notes: <xsl:value-of select="notes" />
    </div>
   </xsl:if>
  </div>
 </xsl:template>
 <xsl:template match="price">
  for $<xsl:value-of select="." />
 </xsl:template>
</xsl:stylesheet>

You can view the 'finished' XML and XSLT.

Next time ... ?

At this point all I really need to do is finish adding the games that I own. While it would be nice to have some other features, it's not completely necessary.

My video games.