Re: [xsl] Specifying element associated with attribute

Subject: Re: [xsl] Specifying element associated with attribute
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Wed, 3 Jan 2001 09:55:10 +0000
Hi Paul,


> When you have a template that matches element nodes, it's easy to
> specify choices based on particular attributes: but how do you do it
> the other way around? That is, you have a template for an attribute
> and you want to specify a choice based on the name of the element in
> which the attribute occurs (ie. test="IF THE NAME OF YOUR ASSOCIATED
> ELEMENT IS 'FOO'"). I've tried numerous permutations involving
> name() and node(), but nothing has worked so far. Please, what's the
> magic expression?

The short answer is, use:

  parent::FOO

The long answer follows:

To go from an attribute to its element involves using the parent::
axis.  So, if the context node is an attribute (as it is in a template
that matches attribute nodes) then you can identify the element that
attribute is on with the expression:

  parent::*

(the element that is the parent of this node) or:

  parent::node()

(the *node* [which actually must be an element in this context] that
is the parent of this node) or the abbreviation of the above:

  ..

To get the name of the parent, you can use the name() function, taking
one of the above expressions as an argument:

  name(..)
  
So, to test whether the name of the parent element is 'FOO' then you
could use:

  name(..) = 'FOO'

This answer will work perfectly well in most cases, but a better
solution becomes apparent if you turn around the phrasing of what
you're after.  You want to know if this attribute has a 'FOO' element
as a parent: is there are parent of this attribute that is a 'FOO'
element?  The expression to get to such a parent is:

  parent::FOO

(the 'FOO' element that is the parent of this node).  If the parent
element is a 'FOO' element, then that node will be returned.  If the
parent element is *not* a 'FOO' element, then no node will be returned
as there is no parent FOO element.  Within a test expression, if a
node is returned the test returns true, if no node is returned the
test returns false.  So (in most situations) the following are
equivalent:

  parent::FOO   is equivalent to   name(..) = 'FOO'

The situation where they are not equivalent is where namespaces are
involved.  The name() of a node gives the exact name for the node
within the XML source.  Look at the following XML:

  <foo:FOO xmlns:foo="http://www.foo.com"; />

In this, the 'FOO' element is in the 'http://www.foo.com' namespace.
The name() of that element is:

  foo:FOO

So, if you are using name() to test the identity of the element, then
you need to use:

  name(..) = 'foo:FOO'

However, it might be that in another document (or even the same
document!) you have an element like:

  <bar:FOO xmlns:bar="http://www.foo.com"; />

The 'FOO' element here is in the same namespace (http://www.foo.com)
but has a different prefix.  Its name is:

  bar:FOO

and it would have to be tested with:

  name(..) = 'bar:FOO'

despite the fact that actually the two FOO elements in the two
documents are meant to be precisely the same.

Fortunately, when you use 'parent::FOO' instead, it takes into account
the fact that the prefix of a namespace isn't important - it's the
*URI* that you have to look at.  If within your XSLT you have declared
the 'foo' prefix to be associated with the 'http://www.foo.com' URI
using:

  xmlns:baz="http://www.foo.com";

then the XPath:

  parent::baz:FOO

will match both the 'foo:FOO' and the 'bar:FOO' elements - it looks
for the equivalence in the namespace *URI* rather than the namespace
*prefix*.

For this reason, it is worth getting into the habit of testing for
nodes called a particular name by testing for whether the node called
that name exists rather than testing whether the node is called that
name.

I hope that helps,

Jeni

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



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


Current Thread