Connecting Tech Pros Worldwide Forums | Help | Site Map

[XSL] Obtaining an attribute from self or ancestor

Ebenezer
Guest
 
Posts: n/a
#1: Oct 21 '08
Hello!

Let's suppose we have an XML with some nested NODE nodes:

<root attr="first">
<node id="1" attr="mike">
<node id="2" />
<node id="3" attr="dave" />
</node>
<node id="4">
<node id="5" attr="peter" />
</node>
</root>

What I want to achieve is to have an XSL that:
- takes a $id variable externally from a PHP script
- finds the node with @id=$id
- outputs the value "@attr" attribute IF PRESENT, otherwise the parent's
"@attr" attribute IF PRESENT, otherwise the grandparent's... recursively
traversing from parent to parent, until an @attr value is found

This won't work for some nodes:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0">
<xsl:output method="html" />
<xsl:template match="root">
<xsl:apply-templates select="//node[@id=$id]" />
</xsl:template>
<xsl:template match="node">
<xsl:choose>
<xsl:when test="not(@attr)"><xsl:apply-templates ".." /></xsl:when>
<xsl:otherwise><xsl:value-of select="@attr" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Martin Honnen
Guest
 
Posts: n/a
#2: Oct 21 '08

re: [XSL] Obtaining an attribute from self or ancestor


Ebenezer wrote:
Quote:
What I want to achieve is to have an XSL that:
- takes a $id variable externally from a PHP script
- finds the node with @id=$id
- outputs the value "@attr" attribute IF PRESENT, otherwise the parent's
"@attr" attribute IF PRESENT, otherwise the grandparent's... recursively
traversing from parent to parent, until an @attr value is found
>
This won't work for some nodes:
>
<?xml version="1.0"?>
<xsl:stylesheet version="1.0">
<xsl:output method="html" />
You need to define the id parameter
<xsl:param name="id"/>
Quote:
<xsl:template match="root">
<xsl:apply-templates select="//node[@id=$id]" />
</xsl:template>
<xsl:template match="node">
<xsl:choose>
<xsl:when test="not(@attr)"><xsl:apply-templates ".."
/></xsl:when>
That is not the correct syntax, you need
<xsl:apply-templates select=".."/>
Also it is not clear what you want to do if you do not find a 'node'
ancestor element with an 'attr' attribute.
If you walk up to the 'root element then the template matching that will
lead to infinite recursion. So perhaps you should better use
<xsl:apply-templates select="parent::node"/>
Or if you want to walk up to the 'root' element as well you need a mode e.g.
<xsl:apply-templates select=".." mode="attribute-check"/>

and then you need to add that mode parameter to the template e.g.


<xsl:template match="root">
<xsl:apply-templates select="//node[@id=$id]"
mode="attribute-check" />
</xsl:template>
<xsl:template match="node | root" mode="attribute-check">
<xsl:choose>
<xsl:when test="not(@attr)"><xsl:apply-templates
select="parent::*" /></xsl:when>
<xsl:otherwise><xsl:value-of select="@attr" /></xsl:otherwise>
</xsl:choose>
</xsl:template>




--

Martin Honnen
http://JavaScript.FAQTs.com/
Ebenezer
Guest
 
Posts: n/a
#3: Oct 21 '08

re: [XSL] Obtaining an attribute from self or ancestor


Martin Honnen ha scritto:
Quote:
Ebenezer wrote:
>
Quote:
>What I want to achieve is to have an XSL that:
>- takes a $id variable externally from a PHP script
>- finds the node with @id=$id
>- outputs the value "@attr" attribute IF PRESENT, otherwise the
>parent's "@attr" attribute IF PRESENT, otherwise the grandparent's...
>recursively traversing from parent to parent, until an @attr value is
>found
>>
>This won't work for some nodes:
>>
><?xml version="1.0"?>
><xsl:stylesheet version="1.0">
> <xsl:output method="html" />
>
You need to define the id parameter
<xsl:param name="id"/>
Are you sure? I don't know if it's a standard behaviour, but other
stylesheet's I've made take the parameters with no effort or definition...
Quote:
That is not the correct syntax, you need
<xsl:apply-templates select=".."/>
Sorry, that was my mistake in pasting the code, it's with "select" indeed...
Quote:
Also it is not clear what you want to do if you do not find a 'node'
ancestor element with an 'attr' attribute.
If you walk up to the 'root element then the template matching that will
lead to infinite recursion. So perhaps you should better use
<xsl:apply-templates select="parent::node"/>
Yes! There was an infinite recursion indeed but I didn't find a clue on
it...
Quote:
Or if you want to walk up to the 'root' element as well you need a mode
e.g.
<xsl:apply-templates select=".." mode="attribute-check"/>
>
and then you need to add that mode parameter to the template e.g.
This info was really valuable. Thanks a lot and excuse me for the multipost.
Pavel Lepin
Guest
 
Posts: n/a
#4: Oct 21 '08

re: [XSL] Obtaining an attribute from self or ancestor



Ebenezer <vaciapairat@spam.comwrote in
<bRgLk.86409$Ca.20678@twister2.libero.it>:
Quote:
<root attr="first">
<node id="1" attr="mike">
<node id="2" />
<node id="3" attr="dave" />
</node>
<node id="4">
<node id="5" attr="peter" />
</node>
</root>
>
What I want to achieve is to have an XSL that:
- takes a $id variable externally from a PHP script
- finds the node with @id=$id
- outputs the value "@attr" attribute IF PRESENT,
otherwise the parent's "@attr" attribute IF PRESENT,
otherwise the grandparent's... recursively traversing from
parent to parent, until an @attr value is found
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="id"/>
<xsl:template match="/">
<xsl:value-of select=
"(//node[@id=$id]/ancestor-or-self::*/@attr)[last()]"
/>
</xsl:template>
</xsl:stylesheet>

HTH, HAND.

--
If we want the average quality of computer programs to rise
by 1000%, all we have to do is carefully to select 90% of
the world's programmers, and shoot them. --Richard
Heathfield in <Sfedncgr46kOPmDanZ2dnUVZ8vCdnZ2d@bt.com>
Dimitre Novatchev
Guest
 
Posts: n/a
#5: Oct 24 '08

re: [XSL] Obtaining an attribute from self or ancestor


Actually, this can be achieved with a single XPath one-liner (even without
XSLT being involved).

Use:

//node[@id = $id]/ancestor-or-self::*[@attr][1]/@attr


Cheers,
Dimitre Novatchev


"Ebenezer" <vaciapairat@spam.comwrote in message
news:bRgLk.86409$Ca.20678@twister2.libero.it...
Quote:
Hello!
>
Let's suppose we have an XML with some nested NODE nodes:
>
<root attr="first">
<node id="1" attr="mike">
<node id="2" />
<node id="3" attr="dave" />
</node>
<node id="4">
<node id="5" attr="peter" />
</node>
</root>
>
What I want to achieve is to have an XSL that:
- takes a $id variable externally from a PHP script
- finds the node with @id=$id
- outputs the value "@attr" attribute IF PRESENT, otherwise the parent's
"@attr" attribute IF PRESENT, otherwise the grandparent's... recursively
traversing from parent to parent, until an @attr value is found
>
This won't work for some nodes:
>
<?xml version="1.0"?>
<xsl:stylesheet version="1.0">
<xsl:output method="html" />
<xsl:template match="root">
<xsl:apply-templates select="//node[@id=$id]" />
</xsl:template>
<xsl:template match="node">
<xsl:choose>
<xsl:when test="not(@attr)"><xsl:apply-templates ".." /></xsl:when>
<xsl:otherwise><xsl:value-of select="@attr" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Richard Tobin
Guest
 
Posts: n/a
#6: Oct 24 '08

re: [XSL] Obtaining an attribute from self or ancestor


In article <490139f9$0$17067$6e1ede2f@read.cnntp.org>,
Dimitre Novatchev <dnovatchev@cnntp.orgwrote:
Quote:
>Actually, this can be achieved with a single XPath one-liner (even without
>XSLT being involved).
>
>Use:
>
//node[@id = $id]/ancestor-or-self::*[@attr][1]/@attr
Or, if you find it clearer not to repeat @attr:

(//node[@id = $id]/ancestor-or-self::*/@attr)[last()]

-- Richard
--
Please remember to mention me / in tapes you leave behind.
Dimitre Novatchev
Guest
 
Posts: n/a
#7: Oct 25 '08

re: [XSL] Obtaining an attribute from self or ancestor



"Richard Tobin" <richard@cogsci.ed.ac.ukwrote in message
news:gdsk17$2hbq$1@pc-news.cogsci.ed.ac.uk...
Quote:
In article <490139f9$0$17067$6e1ede2f@read.cnntp.org>,
Dimitre Novatchev <dnovatchev@cnntp.orgwrote:
>
Quote:
>>Actually, this can be achieved with a single XPath one-liner (even without
>>XSLT being involved).
>>
>>Use:
>>
> //node[@id = $id]/ancestor-or-self::*[@attr][1]/@attr
>
Or, if you find it clearer not to repeat @attr:
>
(//node[@id = $id]/ancestor-or-self::*/@attr)[last()]
>
This may be "clearer" but risks to be less efficient.

Using [1] in a reverse axis is a strong optimization hint and a clever XPath
engine will not traverse the whole way up -- it wil just stop on the first
self-or-ancestor element found that has an "attr" attribute.

Cheers,
Dimitre Novatchev


Richard Tobin
Guest
 
Posts: n/a
#8: Oct 25 '08

re: [XSL] Obtaining an attribute from self or ancestor


In article <490386ab$0$17066$6e1ede2f@read.cnntp.org>,
Dimitre Novatchev <dnovatchev@cnntp.orgwrote:
Quote:
Quote:
> (//node[@id = $id]/ancestor-or-self::*/@attr)[last()]
Quote:
>This may be "clearer" but risks to be less efficient.
>
>Using [1] in a reverse axis is a strong optimization hint and a clever XPath
>engine will not traverse the whole way up -- it wil just stop on the first
>self-or-ancestor element found that has an "attr" attribute.
True, but unlikely to be significant when the axis is ancestor-or-self,
because XML documents tend to be much shallower than they are wide.
For preceding-sibling it would be much more likely to make a difference.

-- Richard
--
Please remember to mention me / in tapes you leave behind.
Closed Thread