Re: [xsl] Selective output of half a tag?

Subject: Re: [xsl] Selective output of half a tag?
From: Peter Davis <pdavis152@xxxxxxxxx>
Date: Tue, 11 Jun 2002 17:03:21 -0700
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

OK, I see.  So you want to place multiple <mytag>'s around groups of <foo>'s.  
Am I right in assuming that

<parent>
  <foo/>
  <foo/>
  <baz/>
  <foo/>
  <foo/>
</parent>

Would become:

<parent>
  <mytag>
    <bar/>
    <bar/>
  </mytag>
  <baz/>
  <mytag>
    <bar/>
    <bar/>
  </mytag>
</parent>

In that case, you will need to apply-templates to all the children of 
<parent>, but make sure that you only select the first out of any group of 
<foo>'s.  Then, you can have a template match <foo>, output <mytag>, and then 
output <bar/> as a child of <mytag> for itself as well as each sibling <foo>.  
The following-sibling axis will help, but the trick is to make sure you only 
get the immediate closest sibling and not any <foo>'s that are in other 
groups.  A recursive template can be used to process each following <foo> one 
at a time.

<xsl:template match="parent">
  <!-- select all nodes that either
    * are not a <foo/>
    * are a <foo/> that is the first in its group
  -->
  <xsl:apply-templates
    select="node()[not(self::foo and preceding-sibling::*[1][self::foo])]"/>
</xsl:template>

<xsl:template match="node()">
  use your normal templates for elements that aren't <foo/>.
</xsl:template>

<xsl:template match="foo">
  <!-- I wasn't clear whether you wanted to output <mytag> around all
    <foo/>'s, or just <foo/>'s that are part of a group with one or more other
    <foo/>'s.  If you want the latter, just remove the <xsl:choose>. -->
  <xsl:choose>
    <xsl:when test="following-sibling::*[1][self::foo]">
      <mytag>
        <xsl:call-template name="output-foo-group"/>
      </mytag>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="output-foo-group"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="output-foo-group">
  <bar>
    <xsl:apply-templates/>
  </bar>
  <xsl:for-each select="following-sibling::*[1][self::foo]">
    <!-- the for-each is only used to change the context,
       but only one <foo> is selected at a time.  the next
       output-foo-group will then do the for-each again. -->
    <xsl:call-template name="output-foo-group"/>
  </xsl:for-each>
</xsl:template>

* this has not been tested at all

HTH

Slight modifications may be required if you have text nodes as children of 
<parent> in the source tree, in which case you would have to use 'node()' 
instead of '*' when testing the preceding- and following-siblings, node() 
also selects text nodes that have nothing but whitespace, which means that 
you would also have to do <xsl:strip-space elements="parent"/> at the top of 
your stylesheet.

On Tuesday 11 June 2002 05:56, Graham Ashton wrote:
> take a series of <foo/> tags in the
> source, convert them all to <bar/> in the output, and then wrap the
> group of <bar/> up in <mytag/>.

Don't think of it in such procedural terms.  Remember that you don't ouptut a 
<mytag> and a </mytag> separately, they are part of the same element.  You 
can only output a <mytag> and then children of the <mytag>.  That means that 
you have to identify all of the children (or at least the start of the group 
of children, as in my example), before you can output the <mytag>.

> So,
>
>   <foo/>
>   <foo/>
>
> becomes
>
>   <mytag>
>     <bar/>
>     <bar/>
>   </mytag>

- -- 
Peter Davis
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)

iD8DBQE9Bo/JNSZCJx7tYycRAk9rAJ9qlTa2hD+whVB+eHTU5wvhc5v9SgCgjMev
KzhsZoskLGtiiJT+QSdnV1g=
=ilZP
-----END PGP SIGNATURE-----


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread