3

Questions concerning the group-adjacent function have already been asked, I know. For some reason though, I can't get this to work.

I inherited project at work that uses xslt and my boss doesn't appreciate how steep the learning curve is for learning xslt and how dissimilar it is from procedural programming.

I'm nonetheless determined to learn it as well as I can, but I still have to work with it while I'm learning. Any help in the solving the below problem would be enormously appreciated.

I have an xml document that's formatted as such:

<document xml:lang="EN">
  <CRPg>text</CRPg>
  <CRPg>text</CRPg>
  <CRPg>text</CRPg>
  <Section/> <!--section marker-->
  <TOCChap>chapter title</TOCChap>
  <TOCAu>chapter author</TOCAu>
  <TOCChap>chapter title</TOCChap>
  <TOCAu>chapter author</TOCAu>
  <TOCChap>chapter title</TOCChap>
  <TOCAu>chapter author</TOCAu>
  <TOCChap>chapter title</TOCChap>
  <TOCAu>chapter author</TOCAu>
  <Section/> <!--section marker-->
  <Txt>body text</Txt>
  <Txt>body text</Txt>
  <Txt>body text</Txt>
  <Txt>body text</Txt>
</document>

It has basically no hierarchy at the moment, and the markers are included to give it structure and are included throughout the file. I'm trying to group all TOC elements and nest them within a containing element:

<document xml:lang="EN">
  <CRPg>text</CRPg>
  <CRPg>text</CRPg>
  <CRPg>text</CRPg>
  <Section/> <!--section marker-->
  <Wrapper> <!-- create new element to wrap around TOC and TOCAuth -->
   <TOCChap>chapter title</TOCChap>
   <TOCAu>chapter author</TOCAu>
   <TOCChap>chapter title</TOCChap>
   <TOCAu>chapter author</TOCAu>
   <TOCChap>chapter title</TOCChap>
   <TOCAu>chapter author</TOCAu>
   <TOCChap>chapter title</TOCChap>
   <TOCAu>chapter author</TOCAu>
  </Wrapper>
  <Section/> <!--section marker-->
  <Txt>body text</Txt>
  <Txt>body text</Txt>
  <Txt>body text</Txt>
  <Txt>body text</Txt>
</document>

This is proving more difficult than I had anticipated. I've tried many different versions of the below code, and I've pasted in the one that seems the least wrong. I realize it's still pretty bad:

<xsl:template match="*">
<xsl:for-each-group select="TOCChap | TOCAuth" group-adjacent="self::TOCChap | self::TOCAuth">
  <xsl:choose>
    <xsl:when test="current-grouping-key()">
      <wrapper>
        <xsl:apply-templates select="current-group()"/>

      </wrapper>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="current-group()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:for-each-group>

Thank you in advance.

Lingamurthy CS
  • 5,412
  • 2
  • 13
  • 21
5150
  • 33
  • 1
  • 4
  • Why do I feel that your Input, Output, and the XSLT are not related to each other? The Input and Output aren't even valid XMLs. Please correct these and post the complete XSLT. – Lingamurthy CS Apr 19 '15 at 01:25

3 Answers3

3

There are three reasons why your attempt doesn't work:

  1. You are selecting the wrong set of nodes;

  2. You are using the union operator | instead of the logical operator or;

  3. There is no element named TOCAuth in your document.

Once you fix these three problems by changing the instruction to:

<xsl:for-each-group select="*" group-adjacent="self::TOCChap or self::TOCAu">

you will see exactly the result you're looking for - see a complete demo here: http://xsltransform.net/jyH9rME

--
BTW, your case is almost exactly identical to the example given in the XSLT 2.0 specification for using group-adjacent - see the last example here: http://www.w3.org/TR/xslt20/#grouping-examples


P.S.

I cannot help wondering why don't you take advantage of the <Section/> markers to place all your groups inside a wrapper - for example, applying:

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"/>

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

<xsl:template match="/document">
    <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:for-each-group select="*" group-starting-with="Section">
            <wrapper>
                <xsl:apply-templates select="current-group()" />
            </wrapper>  
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template match="Section"/>

</xsl:stylesheet>

to your input would result in:

:<?xml version="1.0" encoding="utf-8"?>
<document xml:lang="EN">
   <wrapper>
      <CRPg>text</CRPg>
      <CRPg>text</CRPg>
      <CRPg>text</CRPg>
   </wrapper>
   <wrapper>
      <TOCChap>chapter title</TOCChap>
      <TOCAu>chapter author</TOCAu>
      <TOCChap>chapter title</TOCChap>
      <TOCAu>chapter author</TOCAu>
      <TOCChap>chapter title</TOCChap>
      <TOCAu>chapter author</TOCAu>
      <TOCChap>chapter title</TOCChap>
      <TOCAu>chapter author</TOCAu>
   </wrapper>
   <wrapper>
      <Txt>body text</Txt>
      <Txt>body text</Txt>
      <Txt>body text</Txt>
      <Txt>body text</Txt>
   </wrapper>
</document>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • After some slight editing, this worked, so thanks. Also, I realize wrapping all the
    markers would have made sense, but the nature of the project made that impossible. I wish it was otherwise.
    – 5150 Apr 19 '15 at 19:26
1

You need an XSLT something like this(if there is no hierarchy):

<xsl:stylesheet version="2.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:attribute name="xml:lang">EN</xsl:attribute>
            <xsl:for-each-group select="*" group-adjacent="name() = ('TOCChap','TOCAu')">
                <xsl:choose>
                    <xsl:when test="current-grouping-key()">
                        <Wrapper>
                            <xsl:apply-templates select="current-group()"/>
                        </Wrapper>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:apply-templates select="current-group()"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
Lingamurthy CS
  • 5,412
  • 2
  • 13
  • 21
0

<xsl:template match="document">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:for-each-group select="*" group-adjacent="boolean(self::TOCChap | self::TOCAu)">
            <xsl:choose>
                <xsl:when test="current-grouping-key()">
                    <wrapper>
                        <xsl:apply-templates select="current-group()"/>
                    </wrapper>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:copy>

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