Output all same-name children of a node

Discussion in 'XML' started by Stan, Apr 19, 2007.

  1. Stan

    Stan Guest

    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 ...
    Stan, Apr 19, 2007
    #1
    1. Advertising

  2. 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
    Joseph Kesselman, Apr 19, 2007
    #2
    1. Advertising

  3. Stan

    Pavel Lepin Guest

    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
    Pavel Lepin, Apr 20, 2007
    #3
  4. Stan

    Stan Guest

    On Apr 19, 6:55 pm, Joseph Kesselman <>
    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
    Stan, Apr 20, 2007
    #4
  5. Stan

    roy axenov Guest

    On Apr 20, 6:10 pm, Stan <> wrote:
    > On Apr 19, 6:55 pm, Joseph Kesselman
    > <> 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
    roy axenov, Apr 21, 2007
    #5
    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. Replies:
    0
    Views:
    1,399
  2. Tjerk Wolterink
    Replies:
    2
    Views:
    1,399
    Dimitre Novatchev
    Aug 24, 2006
  3. jiing
    Replies:
    0
    Views:
    2,300
    jiing
    Apr 27, 2007
  4. thecolour
    Replies:
    0
    Views:
    366
    thecolour
    Jun 26, 2007
  5. thecolour
    Replies:
    2
    Views:
    410
    thecolour
    Jun 27, 2007
Loading...

Share This Page