Re: [xsl] Finding the ancestor that declared a namespace?

Subject: Re: [xsl] Finding the ancestor that declared a namespace?
From: Mike Brown <mike@xxxxxxxx>
Date: Mon, 5 Aug 2002 16:40:22 -0600 (MDT)
Roger L. Costello wrote:
> I would like to find the element which declared the bk: namespace
> qualifier.  Further, I want the first ancestor element that declared
> bk:  For example, with this document:
> 
> <bk:BookStore xmlns:bk="http://www.amazon.com";>
>    <bk:Category xmlns:bk="http://www.book-category.com";>
>        <bk:Book>
> 
> The XPath expression should give me the bk:Category element. 

You're more concerned with the prefix than with the namespace name.
This may not be wise. The prefix is not supposed to be important.

It might help if you think of the element names like this:
  <{http://www.amazon.com}BookStore>
    <{http://www.book-category.com}Category>
      <{http://www.book-category.com}Book>

The syntax above is not legal XML, of course, but demonstrates
what the namespace bindings are actually doing. The prefix is
just shorthand for the {namespace name}.

> Here's my attempt (which didn't work):
> 
> <xsl:variable name="ns-elmt" 
>               select="ancestor::*[name(@.)='xmlns:bk'][1]"/>
> 
> This yielded no value for ns-elmt.  Any ideas?  /Roger

Namespace declarations (xmlns attributes) do not exist in the XPath/XSLT data
model. They become namespace nodes, a special type of node separate from, but
similar to, an attribute node. These nodes exist on every element where the
binding is in scope. For example, every element has at least one namespace
node for the 'xml' prefix binding. The namespace-uri() function will tell you
the namespace name for a given element.

This will give you all ancestors in the current node's namespace:

  ancestor::*[namespace-uri()=namespace-uri(current())]

but this is not of much use to you, because you're trying to find where the
declaration was. For that you need to find the *last* ancestor with the
namespace node that binds the current node's prefix to the current node's
namespace name.

  ancestor::*[namespace::*[local-name()=substring-before(name(current()),':') and .=namespace-uri(current())]][last()]

Except this, too, will fail to meet your requirement if you do something like

<bk:Foo xmlns:bk="http://www.book-category.com";>
  <bk:BookStore xmlns:bk="http://www.amazon.com";>
    <bk:Category xmlns:bk="http://www.book-category.com";>
      <bk:Book>

because you would get the bk:Foo element. You really want the last one
with both the right namespace node and a parent with either no 'bk' binding
at all or a different 'bk' binding.

It's possible to do in an XPath expression, but it's getting rather complex
at this point and I think the real solution is to convince you that there 
ought not to be any good reason for wanting to do what you claim you need to 
do. In general, it shouldn't matter where the namespace was declared.

   - Mike
____________________________________________________________________________
  mike j. brown                   |  xml/xslt: http://skew.org/xml/
  denver/boulder, colorado, usa   |  resume: http://skew.org/~mike/resume/

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


Current Thread