[xsl] document tree fragments

Subject: [xsl] document tree fragments
From: Joe Barwell <jbar@xxxxxxxx>
Date: Sat, 11 Oct 2008 21:31:58 +1300
Hello all,

XSLT 1.0 (i.e. Firefox 3.0, etc.)
XSLT 2.0 solutions not suitable.

I have a working xsl stylesheet that transforms an xml file with the
structure:

< wines >
	< wine >...< /wine >+
< /wines >

I want, now, to rewrite my xsl stylesheet to work with a collection of xml files, each of which contain an individual < wine > element--i.e. in place of the single xml < wines > file.

I have succeeded, but with two problems. Firstly, I am unable to separately specify the directory path of the files. Here is my "base" xml file's content:

<docs path="jb">
	<doc filename="jb/wine1.xml" />
	<doc filename="jb/wine2.xml" />
	<doc filename="jb/wine3.xml" />
	...
</docs>

Note that I have provided the directory path within the @filename attribute, but I would prefer to retrieve it from the @path attribute.

Here is the part of my xsl stylesheet that retrieves the contents of my collection of xml files:

<xsl:template match="/docs">
	<xsl:variable name="thePath" select="@path" />
	<xsl:variable name="theWines" select="document(doc/@filename)/wine" />
...

Is there a way to use my $thePath variable, instead of including the directory path inside the @filename attribute? I tried to concatenate, but without success. I want to deal with all my xml files together, rather than using a for-each loop, because of several aggregate processes that I want to apply.


Secondly, I'm not sure I fully understand what my $theWines variable now contains. I believe it is a collection of tree fragments--i.e. there's no single top-most node, is that right? Or is there an implicit / (document) node?


The reason I ask is because I have some templates that rely on processing the preceding axis, and whereas my templates work fine for my < wines > xml file, they do not produce the anticipated results when using my $theWines variable.

This template works with the < wines > xml file, and is called by another template that has already matched the "wines" node:

<xsl:template name="winesByCountry">
<table border="1">
<xsl:for-each select="wine/countries/country[not(name=../../preceding-sibling::wine/countries/country/name)]">
<xsl:sort select="name" order="ascending" />
<xsl:variable name="countryName" select="name" />
<tr><th><xsl:value-of select="$countryName" /></th><td>
<xsl:value-of select="count(/wines/wine/countries/country[name = $countryName])" /></td></tr>
</xsl:for-each>
</table>
</xsl:template>



The following template does not work. It outputs each country multiple times--e.g. 2 wines from Italy are output twice as:


Italy 2
Italy 2

rather than the second time correctly seeing that that country is already present in a preceding node:

<xsl:template name="winesByCountry">
<xsl:param name="theWines" />
<table border="1">
<xsl:for-each select="$theWines/countries/country[not(name=preceding::country/name)]">
<xsl:sort select="name" order="ascending" />
<xsl:variable name="countryName" select="name" />
<tr><th><xsl:value-of select="$countryName" /></th><td>
<xsl:value-of select="count($theWines/countries/country[name=$countryName])" /></td></tr>
</xsl:for-each>
</table>
</xsl:template>



Do I need to wrap the sequence of tree fragments in my $theWines variable inside a node? I tried using the following:


<xsl:variable name="theWines">
	<wines>
	<xsl:value-of select="document(doc/@filename)/wine" />
	</wines>
</xsl:variable>


but Firefox says: Error during XSLT transformation: An XPath expression was expected to return a NodeSet.


Sorry for the length. Any suggestions?

Cheers!

Joe

Current Thread