0

I have the following XSLT 2.0 code to split an XHTML file into chapters:

<xsl:for-each-group
  select=".//html:*[local-name() eq $chapter-tag][1]/(.|following-sibling::*)"
  group-starting-with="html:*[local-name() eq $chapter-tag]">
  ...
</xsl:for-each-group>

(here $chapter-tag is either h1 or h2).

But this code does not work for the following XHTML fragment:

<div class="header">
  <h1>Header</h1>
</div>
<p>...</p>
...

Please help to do the right thing when the header is "buried" inside other tags.

Complete example:

<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Test</title>
  </head>
  <body>
    <div class="container">
      <div class="header">
        <h1>First chapter</h1>
      </div>
      <p>First chapter text.</p>
      <p>Blah, blah, blah...</p>
      <div class="header">
        <h1>Second chapter</h1>
      </div>
      <p>Second chapter text.</p>
      <p>Blah, blah, blah...</p>
    </div>
  </body>
</html>

This should create the following element groups ("chapters"):

      <div class="header">
        <h1>First chapter</h1>
      </div>
      <p>First chapter text.</p>
      <p>Blah, blah, blah...</p>

and

      <div class="header">
        <h1>Second chapter</h1>
      </div>
      <p>Second chapter text.</p>
      <p>Blah, blah, blah...</p>
porton
  • 5,214
  • 11
  • 47
  • 95

3 Answers3

0

If you use

<xsl:template match="div[@class = 'container']">
  <xsl:for-each-group select="*" group-starting-with="div[@class = 'header' and h1]">
     <xsl:copy-of select="current-group()"/>
  </xsl:for-each-group>
</xsl:template>

then for your sample I think the task is solved. It all depends on how regular your input is.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • `class="container"` was just an example. There is no reason to assume that the class will be namely `container`, neither that there is only one level of enclosed divs. Please remove your answer, it is wrong. – porton May 27 '16 at 12:51
  • 2
    If you get a wrong answer from someone of Martin's calibre, it should tell you that there is something wrong with your question. – Michael Kay May 27 '16 at 14:15
0

Still not sure exactly what your requirement is, but would the pattern

group-starting-with="*[descendant-or-self::h1]"

help at all?

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
0

I have solved the problem in my real code. Now it works. Consult my code.

<xsl:template name="split">
  <xsl:variable name="container" select="my:lca(.//html:*[local-name() eq $chapter-tag])"/>
  <xsl:variable name="start" select="$container/node()[descendant-or-self::html:*[local-name() eq $chapter-tag]][1]"/> <!-- the first chapter header in the document -->
  <xsl:for-each-group select="$start|$start/following-sibling::node()"
                      group-starting-with="*[descendant-or-self::html:*[local-name() eq $chapter-tag]]">
    <!-- ... -->
  </xsl:for-each-group>
</xsl:template>

my:lca user-defined function is defined in Finding the lowest common ancestor of an XML node-set

Community
  • 1
  • 1
porton
  • 5,214
  • 11
  • 47
  • 95
  • Whilst this may theoretically answer the question, [it would be preferable](//meta.stackoverflow.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – Daniel Haley May 27 '16 at 15:33