0

I read some answers here on moving nodes down/wrapping elements, but can´t figure out the solution to my problem. Here it is: I want to turn a flat document hierarchy in a "deep" hierarchy with xslt (2.0). The problem is to limit the search for siblings to a certain sibling, in my case: Search all siblings from h1 up to next h1 node.

xml-code

<root>
   <h1>A heading</h1>
   <para>Some text.</para>
   <h2>More text</h2>         
   <table>Content</table>
   <pic>a picture</pic>
   <h1>Another heading.</h1>
   <para>Some text again.</para>
   <para>More text.</para>
   ...
</root>

Real document is much longer, there are up to five levels of headings, there is no fixed order of elements, and in real life I need to move not only the h1-sections, but also the h2-sections and so on. I´ve tried several attempts with copy-of- and similar expressions, but I´m a newbie in xslt. So how to wrap all h1-elements and its siblings up to the next h1-element with a chapter-element and h2-elements in the same way with section-elements?

Desired output is

<root>
   <chapter>
      <h1>A heading</h1>
      <para>Some text.</para>
      <section>
         <h2>More text</h2>         
         <table>Content</table>
         <pic>a picture</pic>
      </section>
   </chapter>
   <chapter>
      <h1>Another heading.</h1>
      <para>Some text again.</para>
      <para>More text.</para>
   </chapter>
   ...
</root>
Pjoern
  • 309
  • 3
  • 11
  • 1
    Possible duplicate of [How would you find all nodes between two H3's using XPATH?](https://stackoverflow.com/questions/3835601/how-would-you-find-all-nodes-between-two-h3s-using-xpath) – derloopkat Apr 13 '18 at 22:51
  • As you mention XSLT 2 I would suggest to look at similar question and answers using a recursive function applying `for-each-group group-starting-with`, like https://stackoverflow.com/a/4547666/252228. – Martin Honnen Apr 14 '18 at 15:06
  • I`ll check the answers referred to here. @derloopkat The intersect operator seems to be exactly what I'm looking for. But I am currently getting an error message when I use it in my editor (Oxygen). Since I am not familiar with xpath and xslt, this will take some time. So right now, I can´t decide if my question is answered. It´ll take some time. – Pjoern Apr 15 '18 at 09:24

1 Answers1

1

The method you can use is two embedded for-each-group commands:

  • One for all elements in root, for groups starting with h1.
  • And the second for the content of the current group (from the first loop), for groups starting with h2.

One important feature of for-each-group coupled with group-starting-with is that when the source content does not start with the tag specified in group-starting-with, then the "starting part" (without the specified starting tag) is the first group.

To distinguish between this "initial" group (starting actually with h1) and groups starting with h2, I used xsl:choose.

So the whole script can look like below:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" encoding="UTF-8" indent="yes" />

  <xsl:template match="root">
    <xsl:copy>
      <xsl:for-each-group select="*" group-starting-with="h1">
        <chapter>
          <xsl:for-each-group select="current-group()" group-starting-with="h2">
            <xsl:choose>
              <xsl:when test="name()='h1'">
                <xsl:copy-of select="current-group()"/>
              </xsl:when>
              <xsl:otherwise>
                <section>
                  <xsl:copy-of select="current-group()"/>
                </section>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:for-each-group>
        </chapter>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>
</xsl:transform>

For a working example see http://xsltransform.net/pNvs5w8

Valdi_Bo
  • 30,023
  • 4
  • 23
  • 41
  • The stylesheet solves my problem. In a first test it also works in my - much more extensive - real document. However, it must be adjusted on several levels. I'm gonna need a little. With this limitation, my upvote for the right answer. – Pjoern Apr 15 '18 at 10:10
  • If you want similar grouping, e.g. for `h3` and `h4`, add more nested `for-each-group` loops. – Valdi_Bo Apr 15 '18 at 10:18