[XSL] Obtaining an attribute from self or ancestor

Discussion in 'XML' started by Ebenezer, Oct 21, 2008.

  1. Ebenezer

    Ebenezer Guest

    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:eek:utput 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:eek:therwise><xsl:value-of select="@attr" /></xsl:eek:therwise>
    </xsl:choose>
    </xsl:template>
    </xsl:stylesheet>
    Ebenezer, Oct 21, 2008
    #1
    1. Advertising

  2. Ebenezer wrote:

    > 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:eek:utput method="html" />


    You need to define the id parameter
    <xsl:param name="id"/>

    > <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:eek:therwise><xsl:value-of select="@attr" /></xsl:eek:therwise>
    </xsl:choose>
    </xsl:template>




    --

    Martin Honnen
    http://JavaScript.FAQTs.com/
    Martin Honnen, Oct 21, 2008
    #2
    1. Advertising

  3. Ebenezer

    Ebenezer Guest

    Martin Honnen ha scritto:
    > Ebenezer wrote:
    >
    >> 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:eek:utput 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...

    > 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...

    > 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...

    > 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.
    Ebenezer, Oct 21, 2008
    #3
  4. Ebenezer

    Pavel Lepin Guest

    Ebenezer <> wrote in
    <bRgLk.86409$>:
    > <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 <>
    Pavel Lepin, Oct 21, 2008
    #4
  5. 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" <> wrote in message
    news:bRgLk.86409$...
    > 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:eek:utput 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:eek:therwise><xsl:value-of select="@attr" /></xsl:eek:therwise>
    > </xsl:choose>
    > </xsl:template>
    > </xsl:stylesheet>
    Dimitre Novatchev, Oct 24, 2008
    #5
  6. In article <490139f9$0$17067$>,
    Dimitre Novatchev <> wrote:

    >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.
    Richard Tobin, Oct 24, 2008
    #6
  7. "Richard Tobin" <> wrote in message
    news:gdsk17$2hbq$...
    > In article <490139f9$0$17067$>,
    > Dimitre Novatchev <> wrote:
    >
    >>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
    Dimitre Novatchev, Oct 25, 2008
    #7
  8. In article <490386ab$0$17066$>,
    Dimitre Novatchev <> wrote:

    >> (//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.


    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.
    Richard Tobin, Oct 25, 2008
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ralf W. Grosse-Kunstleve
    Replies:
    16
    Views:
    580
    Lonnie Princehouse
    Jul 11, 2005
  2. Ralf W. Grosse-Kunstleve
    Replies:
    18
    Views:
    595
    Bengt Richter
    Jul 11, 2005
  3. Ralf W. Grosse-Kunstleve
    Replies:
    2
    Views:
    399
    Dan Sommers
    Jul 12, 2005
  4. falcon
    Replies:
    0
    Views:
    374
    falcon
    Jul 31, 2005
  5. Bart Kastermans
    Replies:
    6
    Views:
    402
    Bart Kastermans
    Jul 13, 2008
Loading...

Share This Page