Recusion in XSL with nested XML

O

oooooo0000000

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
 
J

Johannes Koch

oooooo0000000 said:
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.
 
M

Marrow

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
 
R

Robin Johnson

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

oooooo0000000

I have an XML file of varying nesting depth...
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
 
K

kahrs

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

Marrow

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

oooooo0000000 said:
(e-mail address removed) (oooooo0000000) wrote in message


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
 
W

William Park

oooooo0000000 said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top