[xsl] Re: Keys on multiple element types

Subject: [xsl] Re: Keys on multiple element types
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Tue, 5 Feb 2002 10:02:11 +0000
Hi Ahmad,

> q1. Whats wrong with the stylesheet that it doesnt do this. I think its :-
> select="RECORDA[generate-id(.
>
> but every other path I try ends up with no output.

You're correct that it's that part of the stylesheet that's causing
the problem. The problem is that you're selecting only the RECORDA
elements, but you're interested in the RECORDB and RECORDC elements as
well. One method would be to select the unique ones from all three
types as follows:

  (RECORDA | RECORDB | RECORDC)
    [generate-id(.) = generate-id(key('rows', name)[1])]

Or you could just select all the element children of the current FILES
element, and filter to select only those that appear first in the
document with a particular name, as follows:

  *[generate-id(.) = generate-id(key('rows', name)[1])]

If you didn't want to list all the RECORD names, you could do a
similar thing with the definition of the key - make it match those
elements that are children of the FILES element, no matter what their
name is:

<xsl:key name="rows" match="FILES/*" use="name" />

> q2. Does the vertical bar | mean 'or', and wouldnt it be better to
> use ',' which I think means 'and'? When I tried ',' it threw up
> loads of exceptions.

In an expression, the vertical bar (|) means 'union'. In the example
above, (RECORDA | RECORDB | RECORDC) creates a union of the nodes that
you select with the individual expressions RECORDA, RECORDB and
RECORDC, so you get all RECORDA, RECORDB and RECORDC elements.

In a pattern (a match attribute), the vertical bar gives alternative
patterns. So the pattern RECORDA | RECORDB | RECORDC matches RECORDA,
RECORDB and RECORDC elements.

The comma (,) isn't an operator in XPath 1.0 (it's only used to
separate arguments in a function call). You might have been confused
by recent discussions on this list, because the comma *is* an operator
in XPath 2.0 (at least in the current WDs), where it's used to
construct a sequence. In XPath 2.0, if you do (RECORDA | RECORDB |
RECORDC) then you get the RECORDA, RECORDB and RECORDC elements in
whatever order they appear. If you do (RECORDA, RECORDB, RECORDC) then
you get all the RECORDA elements followed by all the RECORDB elements
followed by all the RECORDC elements. But this doesn't apply in XPath
1.0 (and XSLT 1.0).

To do 'or', use the operator 'or' (e.g. name = 'foo' or name = 'bar').
To do 'and', use the operator 'and' (e.g. name = 'foo' and name =
'bar').

> q.3 If the xml looked like this instead...
>
> <FILES>
>         <RECORDA>
>         <id>13</id><name>Fred</name><project_name>Building</project_name>
>         </RECORDA>
>         <RECORDA>
>         <id>14</id><name>Fred</name><project_name>Building</project_name>
>         </RECORDA>
>         <RECORDB>
>         <id>15</id><name>Fred</name><project_name>Looking</project_name>
>         </RECORDB>
>         <RECORDC>
>         <id>16</id><name>Harry</name><project_name>Writing</project_name>
>         </RECORDC>
> </FILES>
>
> How could I get the result to remove multiple copies e.g. to output
> <id>13</id> but not <id>14</id>

I'm not sure what a 'multiple copy' is in the above. Should the
RECORDB with id 15 be output or not? If so, you need to define a key
that indexes the records on both the name of the element (e.g.
'RECORDA', 'RECORDB' or 'RECORDC') and the name element child (e.g.
'Fred' or 'Harry'). That kind of key might look like:

<xsl:key name="rows" match="FILES/*"
         use="concat(name(), '+', name)" />

Cheers,

Jeni

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


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


Current Thread