XSLT Revealed
This is where we put most of our work in for this go round, building the list items for the main list. Part of the complication is the conditional nested lists that should be included for series.
<li>
<xsl:choose>
<xsl:when test="series">
.
.
.
</xsl:when>
<xsl:otherwise>
This is a book. <xsl:value-of select="title" />
</xsl:otherwise>
</xsl:choose>
</li>
Here we introduce our first xsl:choose operation. It let's us choose between different options. The first option is to use an <xsl:when> tag to test if the item contains a <series> tag. This tells us if it's a series, and we then have a much more detailed process to handle the series and all of its volumes. That's what the first choice is. If the object does not contain a series tag, it had not been dealt with. We want to include all items so we use an <xsl:otherwise> tag to specify what to do with the rest of the objects. Since anything which isn't a series, will be a stand alone title, we simply display the title. And since all this is inside an <xsl:for-each> tag sorted by title, all of the series and book titles are intermixed in perfect alphabetical order. This resolves the key concern of page 3, and why we had to rewrite the XML file; stand alone books and series needed to be sibling elements and books in a series needed not to be. It seemed most reasonable then to nested them inside the series elements.
There's another interesting wrinkle in this. There's actually a display difference between the browsers. The first one I've encountered in XML. The <xsl:when test="series"> checks to see if there is a <series> tag which is a child of the <item> tag selected. This grabs all of the books and series objects, ie the items which contain a <series> tag. Another loop then does processing to grab the series, but not the volumes and continues into another subloop. More on that later. The further processing generates output only for the series entries, not the volumes, so the individual volumes return null values. In Firefox and Opera, that's the end of the story, the null values get thrown out. However... IE is a different story.
IE outputs the null values as empty list items. It doesn't throw them out, resulting in a lot of empty bullet points. (though interestingly, we determined the empty bullets are ordered by title). We were able to fix this issue by eliminating the individual volumes from the original search. We took the preceding for-each loop above and respecified it using an absolute path, <xsl:for-each select="Bibliography/item">. Yes, I know it was only last test that we removed the absolute reference. But that accomplished the display we wanted before we revamped the XML file. In this code, that was the only tweak necessary to resolve the cross browser display problem. The relative path we used on page three worked fine for resolving those issues but not so well here. So out it goes. Next we had to program the series titles to display and create a nested list for each with the books listed in volume# order. We accomplished this with another <xsl:choose>.
<xsl:choose>
<xsl:when test="@format = 'series'">
This is a SERIES, the attribute says so. <xsl:value-of select="title" />
<ul>
<xsl:for-each select="item">
<xsl:sort data-type="number" select="volume" />
<li>
<xsl:value-of select="title" /> (<xsl:value-of select="series" />. Volume <xsl:value-of select="volume" />)
</li>
</xsl:for-each>
</ul>
</xsl:when>
</xsl:choose>
Dealing with the series entries are a little trickier. Since a <series> child tag could indicate a series object or the volumes of that series, we need to specify the series themselves. Otherwise the volumes are listed not only underneath their respective series but a second time alphabetized with the stand alone titles. Therefore we specify a second <xsl:choose> which checks the format attribute. The series object has a format of "series" and passes the test, while the volumes which are format="book" are weeded out. We then declare that we have located the series by it's attribute and list the title of the series to prove it. As a sublist of each series, we NOW want to list the individual volumes in series order. So we create a new list with a for-each loop inside to grab each volume. This means selecting the <item> child tags for the series and sorting them by volume number. We have to specify the data-type as a number, otherwise it alphabetizes the volumes in order as 1, 10, 11, 2, 3, 4... Finally, we create each list item tag, <li> with the following format. Title (Series. Volume volume#)
Note: There was a dubious flirtation with listing the series entries as independent items. At the time, the individual volumes didn't seem to require nesting inside the series items. However, it proved beyond our ability to collect only the volumes for a specific series, so each series wound up with every volume of every series. So I renested the <item> elements of series volumes as children of the appropriate series. This better conveys the relationship between the data in any case; we probably shouldn't have changed it in the first place.
Skip to Main Points