0

I try to group elements by their headings. My current solution has one disadvantage. The next heading and its content is nested in the body tag of the predecessor. Is it possible to nest only the content in the body tag and not the next group? Has anyone an idea or suggestion? It should look like the example desired result.

Source

<?xml version="1.0" encoding="UTF-8"?>
<html>
<title>headline</title>
<body>
  <h1 name="d1e25">H1</h1>
  <p>p</p>
  <h2 name="d1e25">H2</h2>
  <p>p</p>
  <h3 name="d1e25">H3</h3>
  <p>p</p>
  <h3 name="d1e25">H3</h3>
  <p>p</p>
  <h4 name="d1e25">H4</h4>
  <p>p</p>
  <h2 name="d1e25">H2</h2>
  <table></table>
  </body>
</html>

My curent Transformation

<xsl:transform 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf"
  version="2.0">

  <xsl:output method="xml" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

  <xsl:function name="mf:group" as="node()*">
    <xsl:param name="elements" as="element()*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:for-each-group select="$elements" group-starting-with="*[local-name() eq concat('h', $level)]">
      <xsl:choose>
        <xsl:when test="self::*[local-name() eq concat('h', $level)]">
          <topic>
          <xsl:attribute name="id"><xsl:copy-of select="@name"/></xsl:attribute>
            <xsl:element name="title"><xsl:value-of select="." /></xsl:element>
            <body>
              <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/> 
            </body>
          </topic>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="current-group()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@*, node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/html">
   <topic>
     <xsl:attribute name="id">
       <xsl:attribute name="id" select="generate-id()" />
     </xsl:attribute>
       <xsl:copy-of select="title"/>
     <xsl:apply-templates select="body"/>
   </topic>   
  </xsl:template>

  <xsl:template match="body">
    <xsl:sequence select="mf:group(*, 1)"/>
  </xsl:template>

</xsl:transform>

I get this from the following post grouping following-siblings with same name and same attributes causes exception in saxon

My curent Result

<topic id="d1e1">
   <title>headline</title>
   <topic id="d1e25">
      <title>H1</title>
      <body>
         <p>p</p>
         <topic id="d1e25">
            <title>H2</title>
            <body>
               <p>p</p>
               <topic id="d1e25">
                  <title>H3</title>
                  <body>
                     <p>p</p>
                  </body>
               </topic>
               <topic id="d1e25">
                  <title>H3</title>
                  <body>
                     <p>p</p>
                     <topic id="d1e25">
                        <title>H4</title>
                        <body>
                           <p>p</p>
                        </body>
                     </topic>
                  </body>
               </topic>
            </body>
         </topic>
         <topic id="d1e25">
            <title>H2</title>
            <body>
               <table/>
            </body>
         </topic>
      </body>
   </topic>
</topic>

Desired Result

<topic id="d1e1">
   <title>headline</title>
   <topic id="d1e25">
      <title>H1</title>
      <body>
         <p>p</p>
         <topic id="d1e25">
            <title>H2</title>
            <body>
               <p>p</p>
               <topic id="d1e25">
                  <title>H3</title>
                  <body>
                     <p>p</p>
                  </body>
               </topic>
               <topic id="d1e25">
                  <title>H3</title>
                  <body>
                     <p>p</p>
                     <topic id="d1e25">
                        <title>H4</title>
                        <body>
                           <p>p</p>
                        </body>
                     </topic>
                  </body>
               </topic>
            </body>
         </topic>
         <topic id="d1e25">
            <title>H2</title>
            <body>
               <table/>
            </body>
         </topic>
      </body>
   </topic>
</topic>
Community
  • 1
  • 1
Olli
  • 89
  • 7

1 Answers1

0

I have a solution

My Transformation

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf"
  version="2.0">

  <xsl:output method="xml" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

 <xsl:strip-space elements="*"/>

 <xsl:template match="html">
   <document><xsl:apply-templates/></document>
 </xsl:template>

 <xsl:template match="body">
   <xsl:apply-templates select="h1" />
 </xsl:template>

 <xsl:key name="next-headings" match="h6"
          use="generate-id(preceding-sibling::*[self::h1 or self::h2 or
                                               self::h3 or self::h4 or
                                               self::h5][1])" />
 <xsl:key name="next-headings" match="h5"
          use="generate-id(preceding-sibling::*[self::h1 or self::h2 or
                                               self::h3 or self::h4][1])" />
 <xsl:key name="next-headings" match="h4"
          use="generate-id(preceding-sibling::*[self::h1 or self::h2 or
                                               self::h3][1])" />
 <xsl:key name="next-headings" match="h3"
          use="generate-id(preceding-sibling::*[self::h1 or self::h2][1])" />
 <xsl:key name="next-headings" match="h2"
          use="generate-id(preceding-sibling::h1[1])" />

 <xsl:key name="immediate-nodes"
          match="node()[not(self::h1 | self::h2 | self::h3 | self::h4 |
                           self::h5 | self::h6)]"
          use="generate-id(preceding-sibling::*[self::h1 or self::h2 or
                                               self::h3 or self::h4 or
                                               self::h5 or self::h6][1])" />

 <xsl:template match="h1 | h2 | h3 | h4 | h5 | h6">
   <xsl:variable name="vLevel" select="substring-after(name(), 'h')" />
   <topic>
      <xsl:element name="title">
        <xsl:apply-templates />
      </xsl:element>
      <body>
      <xsl:apply-templates select="key('immediate-nodes', generate-id())" />
      </body>
      <xsl:apply-templates select="key('next-headings', generate-id())" />
   </topic>
 </xsl:template>

 <xsl:template match="/*/*/node()" priority="-20">
   <xsl:copy-of select="." />
 </xsl:template>

</xsl:transform>

Result

<document>
   <topic>
      <title>H1</title>
      <body>
         <p>p</p>
      </body>
      <topic>
         <title>H2</title>
         <body>
            <p>p</p>
         </body>
         <topic>
            <title>H3</title>
            <body>
               <p>p</p>
            </body>
         </topic>
         <topic>
            <title>H3</title>
            <body>
               <p>p</p>
            </body>
            <topic>
               <title>H4</title>
               <body>
                  <p>p</p>
               </body>
            </topic>
         </topic>
      </topic>
      <topic>
         <title>H2</title>
         <body>
            <table/>
         </body>
      </topic>
   </topic>
</document>
Olli
  • 89
  • 7