Re: [xsl] cross-reference & xsl:sort - MSXML3 vs MSXML4

Subject: Re: [xsl] cross-reference & xsl:sort - MSXML3 vs MSXML4
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Tue, 16 Apr 2002 09:53:18 +0100
Hi David,

> I want to pass in a collection/@name and get a list of all the
> corresponding part elements (cross refence). Then I want to sort the
> data first by part/@type, then by part/@name. I lifted the sample
> xml from a similar note posted to this list in October 2001. That
> email is duplicated at the bootom. I never did see that problem
> answered.

Whenever you're dealing with cross references, the first thing you
should do is set up a key that indexes the things that you want to get
access to by the value through which you want to access them.

In this case, you want to access the part elements by their id
attribute:

<xsl:key name="parts" match="part" use="@id" />

You can then get a part with a particular id through the key function.
For example, the following gets the part with id '3':

  key('parts', '3')

One of the useful features of the key function is that if you pass in
a node set as the second argument, the XSLT processor goes through
each of the nodes, using their string value to retrieve nodes from the
key, and then unions the results together. So for example, if you're
on a collection element then you can get all the parts referenced by
all the ref element children (through their refid attributes) with:

  key('parts', ref/@refid)

This is really powerful in your case because it immediately gives you
the list of the parts that you want to sort. Your code becomes a lot
simpler:

<xsl:template match="/large-collection/collection">
  <xsl:if test="@name = $MyGroup">
    <xsl:for-each select="key('parts', ref/@refid)">
      <xsl:sort select="@type" />
      <xsl:sort select="@name" />
      <xsl:value-of select="concat('type: ', @type,
                                   '  name: ', @name,
                                   '  id: ', @id, '&#xA;')" />
    </xsl:for-each>
  </xsl:if>
</xsl:template>

By the way, I'd usually only apply templates to the nodes that I was
interested in; in your example, you could apply templates directly to
the collection element whose name is equal to $MyGroup, from within a
template matching the large-collection element:

<xsl:template match="large-collection">
  <xsl:apply-templates select="collection[@name = $MyGroup]" />
</xsl:template>

Then you wouldn't need the xsl:if within the template matching
collection elements.

Cheers,

Jeni

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


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


Current Thread