Recusion in XSL with nested XML

Discussion in 'XML' started by oooooo0000000, Aug 18, 2004.

  1. I have an XML file of varying nesting depth...

    <catalouge>
    <item id="1">
    <name/>
    <item id="23">
    <name/>
    <item id="55">
    <name/>
    </item>
    </item>
    <item id="7">
    <name/>
    </item>
    </catalouge>

    So, each item can contain many items.

    I'm trying to get my XSLT to recursivly loop through each item to
    print it out in block quotes eg
    1 name
    23 name
    55 name
    7 name

    How can I get a <xsl:for-each> loop to go through the whole tree?

    Thanks

    Yours, in confusion

    o0
     
    oooooo0000000, Aug 18, 2004
    #1
    1. Advertising

  2. oooooo0000000 wrote:
    > I have an XML file of varying nesting depth...
    >
    > <catalouge>
    > <item id="1">
    > <name/>
    > <item id="23">
    > <name/>
    > <item id="55">
    > <name/>
    > </item>
    > </item>
    > <item id="7">
    > <name/>
    > </item>
    > </catalouge>
    >
    > So, each item can contain many items.
    >
    > I'm trying to get my XSLT to recursivly loop through each item to
    > print it out in block quotes eg


    I hope you don't mean the HTML blockquote element, which is semantically
    most probably inappropriate for this purpose (as the name says, it's for
    a long block of quotation). Nested lists (ol/ul) would be much better.
    If you want to indent text, use CSS.
    --
    Johannes Koch
    In te domine speravi; non confundar in aeternum.
    (Te Deum, 4th cent.)
     
    Johannes Koch, Aug 18, 2004
    #2
    1. Advertising

  3. oooooo0000000

    Marrow Guest

    Hi,

    Whenever you are dealing with common elements that appear at nested levels
    it is usually best to avoid for-each and go for a recursive push approach
    using applied templates, e.g....

    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:eek:utput method="html" indent="yes"/>
    <xsl:template match="catalouge">
    <html>
    <body>
    <xsl:apply-templates select="item"/>
    </body>
    </html>
    </xsl:template>

    <xsl:template match="item">
    <blockquote>
    <xsl:value-of select="@id"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="name"/>
    <xsl:apply-templates select="item"/>
    </blockquote>
    </xsl:template>
    </xsl:stylesheet>

    HTH
    Marrow
    http://www.marrowsoft.com - home of Xselerator (XSLT IDE and debugger)
    http://www.topxml.com/Xselerator


    "oooooo0000000" <> wrote in message
    news:...
    > I have an XML file of varying nesting depth...
    >
    > <catalouge>
    > <item id="1">
    > <name/>
    > <item id="23">
    > <name/>
    > <item id="55">
    > <name/>
    > </item>
    > </item>
    > <item id="7">
    > <name/>
    > </item>
    > </catalouge>
    >
    > So, each item can contain many items.
    >
    > I'm trying to get my XSLT to recursivly loop through each item to
    > print it out in block quotes eg
    > 1 name
    > 23 name
    > 55 name
    > 7 name
    >
    > How can I get a <xsl:for-each> loop to go through the whole tree?
    >
    > Thanks
    >
    > Yours, in confusion
    >
    > o0
     
    Marrow, Aug 18, 2004
    #3
  4. (oooooo0000000) wrote in message news:<>...
    > I have an XML file of varying nesting depth...
    >
    > <catalouge>
    > <item id="1">
    > <name/>
    > <item id="23">
    > <name/>
    > <item id="55">
    > <name/>
    > </item>
    > </item>
    > <item id="7">
    > <name/>
    > </item>
    > </catalouge>
    >
    > So, each item can contain many items.
    >
    > I'm trying to get my XSLT to recursivly loop through each item to
    > print it out in block quotes eg
    > 1 name
    > 23 name
    > 55 name
    > 7 name
    >
    > How can I get a <xsl:for-each> loop to go through the whole tree?


    Easy; don't. Use apply-templates. If you make this a general habit you
    will write much more efficient code. I think this does it in 1.0:

    (A quick hack, and untested; there is probably a more efficient way
    even if it works, but you get the idea.)

    <xsl:template match="item">
    <xsl:apply-templates select="*">
    </xsl:template>

    <!-- if you're doing what I think you're doing (?) -->
    <xsl:template match="item/*[not(self::item)]">
    <!--
    one of the few occasions I'll use for-each,
    and even then I'd be more inclined to use a 'repeat'
    subtemplate with count(ancestor::item) supplied as a parameter
    -->
    <xsl:for-each select="ancestor::item">
    <xsl:text> </xsl:text>
    </xsl:for-each>

    <xsl:value-of select="concat(parent::item/@id, ' ', name())"/>
    <xsl:text>
    </xsl:text><!-- output an EOL character - I hate breaking indent
    style to do that, so I usually use a global
    text variable called $EOL or similar -->
    </xsl:template>

    --
    Robin Johnson
    Lead Developer, enCircle Solutions Ltd.
    first initial last name at encircle dot co dot uk
     
    Robin Johnson, Aug 18, 2004
    #4
  5. (oooooo0000000) wrote in message news:<>...
    > I have an XML file of varying nesting depth...

    <SNIP>
    >
    > How can I get a <xsl:for-each> loop to go through the whole tree?


    Ok, so I can use <xsl:for-each select="//item"> to pick up all of the
    items (hurrah!) and I can use other XPATHs to get all the children of
    a particular node.

    But, how do I go about "unbalancing" the blockquotes?

    I want

    parent
    child
    grandchild
    other child

    etc. so I tried to use

    <xsl:template match="/">
    <xsl:for-each select="//item">
    <BLOCKQUOTE>
    <xsl:value-of select="title"/>
    </xsl:for-each>
    </BLOCKQUOTE>
    </xsl:template>

    and, of course, with the tags out of order it doesn't work. My brain
    is telling me I'm missing something staggeringly obvious... any clues?

    Thank

    o0
     
    oooooo0000000, Aug 18, 2004
    #5
  6. oooooo0000000

    kahrs Guest

    Hello,

    it is interesting to see that you are talking
    about recursion although such XML structures
    are just ordinary trees. Below you find a non-
    recursive solution.

    > I'm trying to get my XSLT to recursivly loop through each item to
    > print it out in block quotes eg
    > 1 name
    > 23 name
    > 55 name
    > 7 name
    >


    You asked for a solution in XSL.
    I hope you wont flame me if I post a solution
    in a different language. This is a GNU AWK
    script which I have tested:

    BEGIN { XMLMODE=1 }
    XMLSTARTELEM { Depth++ }
    XMLSTARTELEM == "item" { id=XMLATTR["id"] }
    XMLSTARTELEM == "name" { printf("%*s %s\n", 2*Depth, id, "name" ) }
    XMLENDELEM { Depth-- }

    As I said, this one is tested and works, producing
    correctly indented lines:

    1 name
    23 name
    55 name
    7 name

    > How can I get a <xsl:for-each> loop to go through the whole tree?


    In AWK, traversing the tree is the default behavior,
    therefore no recursion is needed.
     
    kahrs, Aug 18, 2004
    #6
  7. oooooo0000000

    Marrow Guest

    Errrrmmm, don't use for-each to begin with? ;)

    "oooooo0000000" <> wrote in message
    news:...
    > (oooooo0000000) wrote in message

    news:<>...
    > > I have an XML file of varying nesting depth...

    > <SNIP>
    > >
    > > How can I get a <xsl:for-each> loop to go through the whole tree?

    >
    > Ok, so I can use <xsl:for-each select="//item"> to pick up all of the
    > items (hurrah!) and I can use other XPATHs to get all the children of
    > a particular node.
    >
    > But, how do I go about "unbalancing" the blockquotes?
    >
    > I want
    >
    > parent
    > child
    > grandchild
    > other child
    >
    > etc. so I tried to use
    >
    > <xsl:template match="/">
    > <xsl:for-each select="//item">
    > <BLOCKQUOTE>
    > <xsl:value-of select="title"/>
    > </xsl:for-each>
    > </BLOCKQUOTE>
    > </xsl:template>
    >
    > and, of course, with the tags out of order it doesn't work. My brain
    > is telling me I'm missing something staggeringly obvious... any clues?
    >
    > Thank
    >
    > o0
     
    Marrow, Aug 18, 2004
    #7
  8. oooooo0000000

    William Park Guest

    oooooo0000000 <> wrote:
    > I have an XML file of varying nesting depth...
    >
    > <catalouge>
    > <item id="1">
    > <name/>
    > <item id="23">
    > <name/>
    > <item id="55">
    > <name/>
    > </item>
    > </item>
    > <item id="7">
    > <name/>
    > </item>
    > </catalouge>
    >
    > So, each item can contain many items.
    >
    > I'm trying to get my XSLT to recursivly loop through each item to
    > print it out in block quotes eg
    > 1 name
    > 23 name
    > 55 name
    > 7 name
    >
    > How can I get a <xsl:for-each> loop to go through the whole tree?
    >
    > Thanks


    You've been given other answers. If you're adventurous, here is Bash
    shell solution.

    start () {
    case $1 in
    item) declare "$@"; itemid=$id ;;
    name) printf "%*c %s name\n" $XML_ELEMENT_DEPTH ' ' $itemid ;;
    esac
    }
    xml -s start "<catalouge>...</catalouge>"

    Ref:
    man 3 printf :)
    http://freshmeat.net/projects/bashdiff/
    help xml

    --
    William Park <>
    Open Geometry Consulting, Toronto, Canada
     
    William Park, Aug 22, 2004
    #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. Kevin Flood
    Replies:
    0
    Views:
    1,057
    Kevin Flood
    Sep 8, 2004
  2. Replies:
    1
    Views:
    3,695
    A. Bolmarcich
    May 27, 2005
  3. puzzlecracker
    Replies:
    2
    Views:
    447
    Rolf Magnus
    May 8, 2005
  4. Ultrus
    Replies:
    3
    Views:
    417
    Stefan Behnel
    Jul 9, 2007
  5. Li Chen

    Is recusion the best?

    Li Chen, Jul 24, 2009, in forum: Ruby
    Replies:
    3
    Views:
    102
    Harry Kakueki
    Jul 24, 2009
Loading...

Share This Page