0

I am in need to Group elements and apply a div element for those group. Also kindly note that element levels are optional and they should be grouped accordingly. The levels "prelims","part","chapter","levela","levelb" and "endmatter" should be grouped. Also note that if "levelc" exists then it should also be grouped

Input:

<toc>
<toc.entry level="prelims">Half Titlepage</toc.entry>
<toc.entry level="prelims">Titlepage</toc.entry>
<toc.entry level="prelims">Imprint</toc.entry>
<toc.entry level="part">Part 1. This is part title</toc.entry>
<toc.entry level="chapter">Chapter 1. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<toc.entry level="levela">This is level a head</toc.entry>
<toc.entry level="levelb">This is level b head</toc.entry>
<toc.entry level="levelb">This is level b head</toc.entry>
<toc.entry level="levelb">This is level b head</toc.entry>
<toc.entry level="chapter">Chapter 2. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<toc.entry level="levela">This is level a head</toc.entry>
<toc.entry level="chapter">Chapter 3. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<toc.entry level="levela">This is level a head</toc.entry>
<toc.entry level="part">Part 2. This is part title</toc.entry>
<toc.entry level="chapter">Chapter 4. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<toc.entry level="chapter">Chapter 5. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<toc.entry level="chapter">Chapter 6. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<toc.entry level="endmatter">References</toc.entry>
<toc.entry level="endmatter">Index</toc.entry>
</toc>

OUTPUT REQUIRED

<toc>
<div class="prelims">
<toc.entry level="prelims">Half Titlepage</toc.entry>
<toc.entry level="prelims">Titlepage</toc.entry>
<toc.entry level="prelims">Imprint</toc.entry>
</div>
<div class="part">
<toc.entry level="part">Part 1. This is part title</toc.entry>
<div class="chapter">
<toc.entry level="chapter">Chapter 1. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<div class="levela">
<toc.entry level="levela">This is level a head</toc.entry>
<div class="levelb">
<toc.entry level="levelb">This is level b head</toc.entry>
<toc.entry level="levelb">This is level b head</toc.entry>
<toc.entry level="levelb">This is level b head</toc.entry>
</div>
</div>
</div>
<div class="chapter">
<toc.entry level="chapter">Chapter 2. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<div class="levela">
<toc.entry level="levela">This is level a head</toc.entry>
</div>
</div>
<div class="chapter">
<toc.entry level="chapter">Chapter 3. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
<div class="levela">
<toc.entry level="levela">This is level a head</toc.entry>
</div>
</div>
</div>
<div class="part">
<toc.entry level="part">Part 2. This is part title</toc.entry>
<div class="chapter">
<toc.entry level="chapter">Chapter 4. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
</div>
<div class="chapter">
<toc.entry level="chapter">Chapter 5. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
</div>
<div class="chapter">
<toc.entry level="chapter">Chapter 6. This is chapter title</toc.entry>
<toc.entry level="author">This is author</toc.entry>
</div>
</div>
<div class="endmatter">
<toc.entry level="endmatter">References</toc.entry>
<toc.entry level="endmatter">Index</toc.entry>
</div>
</toc>

XSLT TRIED

<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns="http://www.w3.org/1999/xhtml" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:RSUITE="http://www.reallysi.com" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:epub="http://www.idpf.org/2007/ops" epub:prefix="index: http://www.index.com/" exclude-result-prefixes="RSUITE">
<xsl:import href="Entity.xsl"/>
<xsl:output method="xml" indent="no"/>


<xsl:template match="toc">
<toc><xsl:apply-templates select="toc.entry"/></toc>
</xsl:template>

<xsl:template match="toc.entry">
<xsl:choose>
<xsl:when test="@level='prelims'"><div class="prelims"><xsl:for-each select="@level='prelims'"><xsl:apply-templates/></xsl:for-each></div></xsl:when>
<xsl:when test="@level='levela'"><xsl:for-each select="@level='levela'"><div class="levela"><xsl:apply-templates/></div></xsl:for-each></xsl:when>
</xsl:choose>
 </xsl:template>

</xsl:stylesheet>
3
  • Which version of XSLT are you using? Also your example is not clear, why is author grouped under chapter? Commented Dec 26, 2014 at 12:04
  • @Rnet: I have edited my question. I am using XSLT 2.0. author is grouped under chapter because it resembles the author of those chapter. Also kindly note that some XML might not have author Commented Dec 26, 2014 at 12:16
  • @siva2012, provided answers are meeting u r requirement, accept those. Commented Dec 28, 2014 at 1:22

2 Answers 2

1

If you want to group, why don't you use the proper grouping tool?

XSLT 2.0

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

<xsl:template match="/toc">
    <toc>
        <!-- PRELIMS -->
        <div class="prelims">
            <xsl:copy-of select="toc.entry[@level='prelims']"/>
        </div>
        <!-- PARTS -->
        <xsl:for-each-group select="toc.entry[not(@level='prelims' or @level='endmatter')]" group-starting-with="toc.entry[@level='part']">
            <div class="part">
                <xsl:copy-of select="current-group()[@level='part']"/>
                <!-- CHAPTERS -->
                <xsl:for-each-group select="current-group()[not(@level='part')]" group-starting-with="toc.entry[@level='chapter']">
                    <div class="chapter">
                        <!-- CHAPTER & AUTHOR -->
                        <xsl:copy-of select="current-group()[@level='chapter' or @level='author']"/>
                        <!-- LEVEL A (OPTIONAL) -->
                        <xsl:if test="current-group()[@level='levela']">
                            <div class="'levela">
                                <xsl:copy-of select="current-group()[@level='levela']"/>
                                <!-- LEVEL B (OPTIONAL) -->
                                <xsl:if test="current-group()[@level='levelb']">
                                    <div class="'levelb">
                                        <xsl:copy-of select="current-group()[@level='levelb']"/>
                                        <!-- LEVEL C (OPTIONAL) -->
                                        <xsl:if test="current-group()[@level='levelc']">
                                            <div class="'levelc">
                                                <xsl:copy-of select="current-group()[@level='levelc']"/>
                                            </div>  
                                        </xsl:if>
                                    </div>
                                </xsl:if>
                            </div>
                        </xsl:if>
                    </div>
                </xsl:for-each-group>
            </div>
        </xsl:for-each-group>
        <!-- END MATTER -->
        <div class="endmatter">
            <xsl:copy-of select="toc.entry[@level='endmatter']"/>
        </div>
    </toc>
</xsl:template>

</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

4 Comments

nice code using grouping, +1, 'levelb' nest under 'levela'.
@michael.hor257k Thanks a lot for your coding. It helped me a lot to understand the concept of grouping
@RudramuniTP I have only now understood what you meant. Thanks, I have fixed it.
@michael.hor257k, Sir, thanks for u r suggestion as well as updation.
1

Following XSLT

<?xml version='1.0'?>
<xsl:stylesheet version="2.0"  xmlns="http://www.w3.org/1999/xhtml"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
     xmlns:RSUITE="http://www.reallysi.com" 
     xmlns:m="http://www.w3.org/1998/Math/MathML" 
     xmlns:epub="http://www.idpf.org/2007/ops" 
     epub:prefix="index: http://www.index.com/" 
     exclude-result-prefixes="RSUITE epub m">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <xsl:template match="/">
    <toc>
      <div class="prelims">
        <xsl:apply-templates select="//toc.entry[@level='prelims']"/>
      </div>
      <xsl:for-each select="//toc.entry[@level='part']">
        <div class="part">
          <xsl:apply-templates select="." mode="part">
            <xsl:with-param name="positionPart" select="position()"/>
          </xsl:apply-templates>
        </div>
      </xsl:for-each>
      <div class="endmatter">
        <xsl:apply-templates select="//toc.entry[@level='endmatter']"/>
      </div>
    </toc>
  </xsl:template>
  <xsl:template match="toc.entry" mode="part">
  <xsl:param name="positionPart"/>
   <xsl:apply-templates select="."/>
   <xsl:for-each select="following-sibling::*[@level='chapter'
        and not(count(preceding-sibling::*[@level='part']) > $positionPart)]">
      <xsl:variable name="positionChapter" select="position()"/>
        <div class="chapter">
          <xsl:apply-templates select="." mode="chapter">
            <xsl:with-param name="positionPart" select="$positionPart"/>
            <xsl:with-param name="positionChapter" select="$positionChapter"/>
          </xsl:apply-templates>
        </div>
   </xsl:for-each>
  </xsl:template>
  <xsl:template match="toc.entry" mode="chapter" >
  <xsl:param name="positionPart"/>
  <xsl:param name="positionChapter"/>
  <xsl:apply-templates select="."/>
  <xsl:apply-templates select="following-sibling::*[1][@level='author']"/>
    <xsl:for-each select="following-sibling::*[@level='levela'
      and not(count(preceding-sibling::*[@level='chapter']) > $positionChapter)]">
      <div class="levela">
        <xsl:apply-templates select="."/>
        <xsl:if test="following-sibling::*[@level='levelb'
             and not(count(preceding-sibling::*[@level='chapter']) > $positionChapter)]">
          <div class="levelb">
            <xsl:for-each select="following-sibling::*[@level='levelb'
                 and not(count(preceding-sibling::*[@level='chapter']) > $positionChapter)]">
              <xsl:apply-templates select="."/>
            </xsl:for-each>
          </div>
        </xsl:if>
      </div>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="toc.entry">
    <toc.entry>
      <xsl:attribute name="level" select="@level"/>
      <xsl:value-of select="."/>
    </toc.entry>
  </xsl:template>
</xsl:stylesheet>

when applied to your input XML produces the output

<toc xmlns="http://www.w3.org/1999/xhtml">
<div class="prelims">
  <toc.entry level="prelims">Half Titlepage</toc.entry>
  <toc.entry level="prelims">Titlepage</toc.entry>
  <toc.entry level="prelims">Imprint</toc.entry>
</div>
<div class="part">
  <toc.entry level="part">Part 1. This is part title</toc.entry>
  <div class="chapter">
     <toc.entry level="chapter">Chapter 1. This is chapter title</toc.entry>
     <toc.entry level="author">This is author</toc.entry>
     <div class="levela">
        <toc.entry level="levela">This is level a head</toc.entry>
        <div class="levelb">
           <toc.entry level="levelb">This is level b head</toc.entry>
           <toc.entry level="levelb">This is level b head</toc.entry>
           <toc.entry level="levelb">This is level b head</toc.entry>
        </div>
     </div>
  </div>
  <div class="chapter">
     <toc.entry level="chapter">Chapter 2. This is chapter title</toc.entry>
     <toc.entry level="author">This is author</toc.entry>
     <div class="levela">
        <toc.entry level="levela">This is level a head</toc.entry>
     </div>
  </div>
  <div class="chapter">
     <toc.entry level="chapter">Chapter 3. This is chapter title</toc.entry>
     <toc.entry level="author">This is author</toc.entry>
     <div class="levela">
        <toc.entry level="levela">This is level a head</toc.entry>
     </div>
  </div>
</div>
<div class="part">
  <toc.entry level="part">Part 2. This is part title</toc.entry>
  <div class="chapter">
     <toc.entry level="chapter">Chapter 4. This is chapter title</toc.entry>
     <toc.entry level="author">This is author</toc.entry>
  </div>
  <div class="chapter">
     <toc.entry level="chapter">Chapter 5. This is chapter title</toc.entry>
     <toc.entry level="author">This is author</toc.entry>
  </div>
  <div class="chapter">
     <toc.entry level="chapter">Chapter 6. This is chapter title</toc.entry>
     <toc.entry level="author">This is author</toc.entry>
  </div>
  </div>
  <div class="endmatter">
   <toc.entry level="endmatter">References</toc.entry>
   <toc.entry level="endmatter">Index</toc.entry>
  </div>
</toc>

I've kept the namespaces from your example XSLT, though they were not needed for the online XSLT processor I used (Saxon 9.5.1.6). In case it would be necessary to remove the xmlns-namespace from the toc root element in the output, it's possible to remove it from the stylesheet declaration.

Explanation: As the elements with the prelims and the endmatter level should be displayed at the beginning / the end of the XML, they are just applied in the main template matching the root in div containers with the appropriate class.
All part levels are applied to the template with the mode="part" and the position of the current part as parameter. To generate all chapters that belong to the current part, templates are only applied to all following sibling with the level chapter that do not have more preceding siblings with the level part as the current part element:

<xsl:for-each select="following-sibling::*[@level='chapter'
    and not(count(preceding-sibling::*[@level='part']) > $positionPart)]">
    ....

with the position of the current chapter as additional parameter.
The template mode="chapter" will only generate an author in case the next sibling of the chapter has the level author:

<xsl:apply-templates select="following-sibling::*[1][@level='author']"/>  

To generate only the levela level elements that belong to the current chapter, only the levela elements are selected that don't have more preceding chapters as the current chapter:

<xsl:for-each select="following-sibling::*[@level='levela'
     and not(count(preceding-sibling::*[@level='chapter']) > $positionChapter)]">
     ...

Similar approach was taken for the levelb level elements.
As it's not clear if it's possible that chapters don't have levela elements, I've only added a check for levelb before writing the div container:

<xsl:if test="following-sibling::*[@level='levelb'
    and not(count(preceding-sibling::*[@level='chapter']) > $positionChapter)]">
    <div class="levelb">
    ...

I've left the part with possible levelc elements out - in case this kind of approach would work for you it shouldn't be too much of a problem to add it.
In addition I want to notice that the result depends on the XSLT processor you use - e.g. when switching to Saxon 6.5.5, above XSLT won't generate the values for the level attributes.
For convenience or further testings I saved above example here: http://xsltransform.net/eiZQaF7

1 Comment

hats off to u r logics, +1.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.