Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > XML > Output all same-name children of a node

Reply
Thread Tools

Output all same-name children of a node

 
 
Stan
Guest
Posts: n/a
 
      04-19-2007
Forgive a newbie, please:

I've got XML like this:
<body>
<block>
<p>content of p1</p>
<p>content of p2</p>
<p>content of p3</p>
...
<p>content of pN</p>
</block>
</body>

How can I write an XSLT to spit out the contents of every <p> element,
separated by <p> tags?

This generates the entire contents of the <block> element, of course,
but all in a blob:
<xsl:for-each select="body">
<xsl:value-of select="block">
</xsl:value-of>
</xsl:for-each>

And this generates the contents of the first <p> element followed by a
<p> tag, but not the remaining <p> elements (again, of course):
<xsl:for-each select="nitf/body/body.content/block">
<xsl:value-of select="p"></xsl:value-of>
<p></p>
</xsl:for-each>

I'm sure I'm missing something very simple ...

 
Reply With Quote
 
 
 
 
Joseph Kesselman
Guest
Posts: n/a
 
      04-19-2007
Stan wrote:
> How can I write an XSLT to spit out the contents of every <p> element,
> separated by <p> tags?


What you've already got is, in fact, more correct HTML than what you're
trying to produce, since <p> means "start a paragraph". End-paragraph
(</p>) tags also exist in HTML and really should be used, but people
have gotten sloppy abou that since HTML processors will try to guess
where they should have been and recreate them.

If you really insist on doing what you've asked for, it's possible. What
you want to do is rip the content out of every <p> element and replace
it with the content preceeded by (or followed by> a <p> element. Your
second attempt is close, but value-of returns only the value of the
*first* node matching the selection. If you want to process them all,
you need another for-each... or, better, you need to replace your
for-eaches with apply-template calls and let XSLT's normal recursive
processing do the work for you.

For example, I'd solve this with:
<xsl:template match="p">
<xsl:apply-templates/>
<p/>
</xsl:template>

which is a template that matches <p> elements and replaces them with
their content followed by an empty paragraph, sloppy-HTML style. Note
that because this uses apply-templates to retrieve its content, rather
than value-of, it leaves you free to plug in templates to process other
markup that might be present within the paragraph's content.

Whether that's the best solution will depend on what the rest of your
stylesheet looks like and whether you care about the extra <p> at the
end (which can be suppressed with some additional coding).


Learn to think in terms of apply-templates rather than explicit for-each
loops, at least as your first solution. Use for-each only if you really
need a special-case "anonymous local template".

--
Joe Kesselman / Beware the fury of a patient man. -- John Dryden
 
Reply With Quote
 
 
 
 
Pavel Lepin
Guest
Posts: n/a
 
      04-20-2007

Joseph Kesselman wrote:
> Stan wrote:
>> How can I write an XSLT to spit out the contents of every
>> <p> element, separated by <p> tags?


[solution]

> which is a template that matches <p> elements and replaces
> them with their content followed by an empty paragraph,
> sloppy-HTML style.


Note that as far as I can tell the HTML serializer is quite
likely to spit out '<p></p>' instead of just '<p>' for the
result tree like that, since p element's content model is
not empty.

Anyway, it just doesn't make much sense to produce sloppy
HTML (that, depending on context, may be outright invalid),
when it's so easy to do the Right Thing with XSLT.

> Use for-each only if you really need a special-case
> "anonymous local template".


In seculum seculorum, amen.

The biggest mystery of XSLT for me is why the omniscient
alpha geeks who wrote the specs called the darned
thing 'for-each'. They probably understood what it really
was far better than I do, after all.

--
Pavel Lepin
 
Reply With Quote
 
Stan
Guest
Posts: n/a
 
      04-20-2007
On Apr 19, 6:55 pm, Joseph Kesselman <(E-Mail Removed)>
wrote:
> Stan wrote:
> > How can I write an XSLT to spit out the contents of every <p> element,
> > separated by <p> tags?

>
> What you've already got is, in fact, more correct HTML than what you're
> trying to produce


I see that my example wasn't clear. I'm not trying to produce empty
paragraphs; I'm trying to put the contents of each <p> node in the XML
inside a <p> tag. I actually expected that my first block of code
would work; I expected the <p> nodes in the <block> node to render the
same way <p> HTML tags render, but they don't. This code:
<xsl:for-each select="body">
<xsl:value-of select="block">
</xsl:value-of>
</xsl:for-each>
produces this:
content of p1content of p2content of p3...content of pN
when I want this:
p>content of p1</p>
<p>content of p2</p>
<p>content of p3</p>
...
<p>content of pN</p>

BUT, this:

> better, you need to replace your
> for-eaches with apply-template calls and let XSLT's normal recursive
> processing do the work for you.
>
> For example, I'd solve this with:
> <xsl:template match="p">
> <xsl:apply-templates/>
> <p/>
> </xsl:template>
>


was an ah-hah moment for me. Works perfectly, and advanced my (so far
very limited) understanding considerably. Thank you very much!!
-Stan

 
Reply With Quote
 
roy axenov
Guest
Posts: n/a
 
      04-21-2007
On Apr 20, 6:10 pm, Stan <(E-Mail Removed)> wrote:
> On Apr 19, 6:55 pm, Joseph Kesselman
> <(E-Mail Removed)> wrote:
>
> > Stan wrote:
> > > How can I write an XSLT to spit out the contents of
> > > every <p> element, separated by <p> tags?

>
> > What you've already got is, in fact, more correct HTML
> > than what you're trying to produce

>
> I see that my example wasn't clear. I'm not trying to
> produce empty paragraphs; I'm trying to put the contents
> of each <p> node in the XML inside a <p> tag.


Forget the tags. There are no tags, only nodes. Once you
see the nodes, you see the matrix. But I think I understand
what you're getting at.

> BUT, this:
>
> > For example, I'd solve this with:
> > <xsl:template match="p">
> > <xsl:apply-templates/>
> > <p/>
> > </xsl:template>

>
> was an ah-hah moment for me. Works perfectly, and
> advanced my (so far very limited) understanding
> considerably.


And now consider this:

<xsl:template match="p">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>

Unless I'm much mistaken, this should work even better for
you, and should be even more of an eye-opener. (It also was
what Joseph meant when he said your XML was closer to valid
HTML than what you seemed to want as an output of your
transformation.)

--
roy axenov

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
I want to select all the node names beneath a given node thecolour XML 2 06-27-2007 10:46 AM
I want to select all the node names beneath a given node thecolour XML 0 06-26-2007 04:01 PM
xsl variable $node/text() but $node can non-node-set help! Tjerk Wolterink XML 2 08-24-2006 03:28 AM
How to set the node indent property between the parent node and the leaf node viveknatani@gmail.com ASP .Net 0 02-13-2006 07:11 PM
XPath expression that gets a node with 2 particular children Jeff XML 2 07-05-2003 06:20 PM



Advertisments