Re: [xsl] using XSLT to transform a XML recordset

Subject: Re: [xsl] using XSLT to transform a XML recordset
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Wed, 22 Aug 2001 17:44:24 +0100
Hi Svend,

> notice how, each bundle, being the main identifier, doesn't always
> have the same number of resources. the idea being, that we want the
> base to be flexible, so no set amount of values have been defined.
> Just that it always contains a reskey (resourcekey), and a resvalue
> (resourcevalue), and these properties/values are tied to a bundle
> name, in the above example, "svendtofte" and "mikkel".

Say that you knew the name of the bundle, and that it was held in the
$bundle variable. In order to create the relevant attributes on the
element for that bundle, you'd need a quick way of getting the row
elements whose bundle attribute was $bundle. Once you got those, it
would be easy to use a predicate to find the one with the appropriate
reskey.

A 'quick way' of getting the row elements for a particular bundle is
to use xsl:key. Set up the key to index the row elements by their
bundle attribute, with an xsl:key top-level element, as follows:

<xsl:key name="rows" match="row" use="@bundle" />

With the key defined, you can find the rows for the $bundle bundle
with:

  key('rows', $bundle)

So you could create the element for the bundle with something like:

  <xsl:variable name="rows" select="key('rows', $bundle)" />
  <element bundle="{$bundle}"
           description="{$rows[@reskey = 'description']/@resvalue}"
           title="{$rows[@reskey = 'title']/@resvalue}"
           link="{$rows[@reskey = 'link']/@resvalue}"
           de="{$rows[@reskey = 'de']/@resvalue}"
           blah="{$rows[@reskey = 'blah']/@resvalue}" />

You can use the same key to get the possible values for the $bundle
variable using the Muenchian Method. If you go through each of the row
elements, you can find the ones that are the first with each
particular value for $bundle by seeing whether the row is the first
node that's returned by the key for their particular bundle. You can
do this with:

  <xsl:for-each select="row[count(.|key('rows', @bundle)[1]) = 1]">
    <xsl:variable name="bundle" select="@bundle" />
    <xsl:variable name="rows" select="key('rows', $bundle)" />
    <element bundle="{$bundle}"
             description="{$rows[@reskey = 'description']/@resvalue}"
             title="{$rows[@reskey = 'title']/@resvalue}"
             link="{$rows[@reskey = 'link']/@resvalue}"
             de="{$rows[@reskey = 'de']/@resvalue}"
             blah="{$rows[@reskey = 'blah']/@resvalue}" />
  </xsl:for-each>


You can use the same method to work out what possible values there are
for the @reskey attributes, store them in a global variable, and then
iterate over them when you need to create the element, if that's
necessary.
  
To improve performance, you could also use a separate key to retrieve
the resvalue attribute for a particular bundle+reskey combination, if
you wanted:

<xsl:key name="resvalues" match="row/@resvalue"
         use="concat(../@bundle, ':', ../@reskey)" />

and then retrieve the relevant values as follows:

  <xsl:for-each select="row[count(.|key('rows', @bundle)[1]) = 1]">
    <xsl:variable name="bundle" select="@bundle" />
    <element bundle="{$bundle}"
             description="{key('resvalues',
                               concat($bundle, ':description'))}"
             title="{key('resvalues', concat($bundle, ':title'))}"
             link="{key('resvalues', concat($bundle, ':link'))}"
             de="{key('resvalues', concat($bundle, ':de'))}"
             blah="{key('resvalues', concat($bundle, ':blah'))}"
  </xsl:for-each>

So yes, it's possible, and in a one-pass solution, though it's not
particularly straightforward (hopefully XSLT 2.0 will help with that
eventually).
  
Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread