0
votes

I have a flat xml file that needs to be converted to hierarchical. I used the nested grouping idea from here xsl:for-each-group help needed. It's working for the most part except for a couple of issues:

1) The elements root1 and root2 are not showing up.

2) The location of element NFActy is incorrect. The first NFActy should be between the 2 Rot elements.

3) The Duty & NFActy elements are treated as one group for calculating the SequenceCount.

4) For Leg elements each set of Leg elements within one duty should be grouped for SequenceCount for leg elements.

Any help is really appreciated.

Thanks.

Input XML

<root>
<root1>1234</root1>
<root2>5678</root2>
<Schedule>
    <a>line 1</a>
    <b>line 2</b>
    <c>line 3</c>
</Schedule>
<Rot>
    <d>line 4</d>
    <e>line 5</e>
    <f>line 6</f>
    <g>line 7</g>
</Rot>
<Duty>
    <h>line 8</h>
    <i>line 9</i>
    <j>line 10</j>
    <k>line 11</k>
    <l>line 12</l>
</Duty>
<Leg>
    <m>line 13</m>
    <n>line 14</n>
    <o>line 15</o>
</Leg>
<Leg>
    <m>line 13</m>
    <n>line 14</n>
    <o>line 15</o>
</Leg>
<NFActy>
    <d>line 214</d>
    <e>line 215</e>
    <f>line 216</f>
    <g>line 217</g>
</NFActy>
<Rot>
    <d>line 19</d>
    <e>line 20</e>
    <f>line 21</f>
    <g>line 22</g>
</Rot>
<Duty>
    <h>line 23</h>
    <i>line 24</i>
    <j>line 25</j>
    <k>line 26</k>
    <l>line 27</l>
</Duty>
<Leg>
    <m>line 28</m>
    <n>line 29</n>
    <o>line 30</o>
</Leg>
<Leg>
    <m>line 13</m>
    <n>line 14</n>
    <o>line 15</o>
</Leg>
<NFActy>
    <d>line 224</d>
    <e>line 225</e>
    <f>line 226</f>
    <g>line 227</g>
</NFActy>

Desired Output XML

<root>
<root1>1234</root1>
<root2>5678</root2>
<Schedule>
    <a>line 1</a>
    <b>line 2</b>
    <c>line 3</c>
    <Rot>
        <d>line 4</d>
        <e>line 5</e>
        <f>line 6</f>
        <g>line 7</g>
        <Duty>
            <SequenceCount>1</SequenceCount>
            <h>line 8</h>
            <i>line 9</i>
            <j>line 10</j>
            <k>line 11</k>
            <l>line 12</l>
            <Leg>
                <SequenceCount>1</SequenceCount>
                <m>line 13</m>
                <n>line 14</n>
                <o>line 15</o>
            </Leg>
            <Leg>
                <SequenceCount>2</SequenceCount>
                <m>line 13</m>
                <n>line 14</n>
                <o>line 15</o>
            </Leg>
        </Duty> 
    </Rot>
    <NFActy>
        <SequenceCount>2</SequenceCount>
        <d>line 214</d>
        <e>line 215</e>
        <f>line 216</f>
        <g>line 217</g>
    </NFActy>
    <Rot>
        <d>line 19</d>
        <e>line 20</e>
        <f>line 21</f>
        <g>line 22</g>
        <Duty>
            <SequenceCount>3</SequenceCount>
            <h>line 23</h>
            <i>line 24</i>
            <j>line 25</j>
            <k>line 26</k>
            <l>line 27</l>
            <Leg>
                <SequenceCount>1</SequenceCount>
                <m>line 28</m>
                <n>line 29</n>
                <o>line 30</o>
            </Leg>
            <Leg>
                <SequenceCount>2</SequenceCount>
                <m>line 13</m>
                <n>line 14</n>
                <o>line 15</o>
            /Leg>
        </Duty>
    </Rot>  
    <NFActy>
        <SequenceCount>4</SequenceCount>
        <d>line 224</d>
        <e>line 225</e>
        <f>line 226</f>
        <g>line 227</g>
    </NFActy>
</Schedule>

My XSL

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

    <xsl:output indent="yes" />

    <xsl:template match="/">
        <xsl:for-each-group select="*" group-starting-with="root">
            <root>
                <xsl:for-each-group select="*" group-starting-with="*[name(.)='Schedule']">
                    <xsl:choose>
                        <xsl:when test="name(.)='Schedule'">
                            <Schedule>
                                <xsl:apply-templates />
                                <xsl:for-each-group select="current-group()" group-starting-with="*[name(.)='Rot']">
                                    <xsl:choose>
                                        <xsl:when test="name(.)='Rot'">
                                            <Rot>
                                                <xsl:apply-templates />
                                                <xsl:for-each-group select="current-group()" group-starting-with="*[name(.)='Duty']">
                                                    <xsl:choose>
                                                        <xsl:when test="name(.)='Duty'">
                                                            <Duty>
                                                                <xsl:element name="SequenceCount"><xsl:number count="Duty|NFActy" level="any"/></xsl:element>
                                                                <xsl:apply-templates />
                                                                <xsl:for-each-group select="current-group()" group-starting-with="*[name(.)='Leg']">
                                                                    <xsl:choose>
                                                                        <xsl:when test="name(.)='Leg'">
                                                                            <Leg>
                                                                                <xsl:element name="SequenceCount"><xsl:number value="position() - 1"/></xsl:element>
                                                                                <xsl:apply-templates />
                                                                            </Leg>
                                                                        </xsl:when>
                                                                    </xsl:choose>
                                                                </xsl:for-each-group>
                                                            </Duty>
                                                        </xsl:when>
                                                    </xsl:choose>
                                                </xsl:for-each-group>
                                            </Rot>
                                        </xsl:when>
                                    </xsl:choose>
                                </xsl:for-each-group>
                                <xsl:for-each-group select="current-group()" group-starting-with="*[name(.)='NFActy']">
                                    <xsl:choose>
                                        <xsl:when test="name(.)='NFActy'">
                                            <NFActy>
                                                <xsl:element name="SequenceCount"><xsl:number count="Duty|NFActy" level="any"/></xsl:element>
                                                <xsl:apply-templates />
                                            </NFActy>
                                        </xsl:when>
                                    </xsl:choose>
                                </xsl:for-each-group>
                            </Schedule>
                        </xsl:when>
                    </xsl:choose>
                </xsl:for-each-group>
            </root>
        </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="node()">
        <xsl:copy>
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
1

1 Answers

0
votes

Here is my edit of your code, you need to make sure you process all elements at the same time and not as you had with sequential for-each-group.

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

    <xsl:output indent="yes" />

    <xsl:template match="root">
        <xsl:copy>
                <xsl:for-each-group select="*" group-starting-with="Schedule">
                    <xsl:choose>
                        <xsl:when test="self::Schedule">
                            <Schedule>
                                <xsl:apply-templates />
                                <xsl:for-each-group select="current-group() except ." group-starting-with="Rot | NFActy">
                                    <xsl:choose>
                                        <xsl:when test="self::Rot | self::NFActy">
                                            <xsl:copy>
                                                <xsl:if test="self::NFActy">
                                                  <SequenceCount><xsl:number count="Duty | NFActy" level="any"/></SequenceCount>
                                                </xsl:if>
                                                <xsl:apply-templates />
                                                <xsl:for-each-group select="current-group() except ." group-starting-with="Duty">
                                                    <xsl:choose>
                                                        <xsl:when test="self::Duty">
                                                            <xsl:copy>
                                                                <SequenceCount><xsl:number count="Duty | NFActy" level="any"/></SequenceCount>
                                                                <xsl:apply-templates />
                                                                <xsl:for-each-group select="current-group() except ." group-starting-with="Leg">
                                                                    <xsl:choose>
                                                                        <xsl:when test="self::Leg">
                                                                            <Leg>
                                                                                <SequenceCount><xsl:number value="position()"/></SequenceCount>
                                                                                <xsl:apply-templates />
                                                                            </Leg>
                                                                        </xsl:when>
                                                                    </xsl:choose>
                                                                </xsl:for-each-group>
                                                            </xsl:copy>
                                                        </xsl:when>
                                                    </xsl:choose>
                                                </xsl:for-each-group>
                                            </xsl:copy>
                                        </xsl:when>
                                    </xsl:choose>
                                </xsl:for-each-group>
                            </Schedule>
                        </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>
</xsl:stylesheet>

When I apply above stylesheet with Saxon 9.5 to the input

<root>
<root1>1234</root1>
<root2>5678</root2>
<Schedule>
    <a>line 1</a>
    <b>line 2</b>
    <c>line 3</c>
</Schedule>
<Rot>
    <d>line 4</d>
    <e>line 5</e>
    <f>line 6</f>
    <g>line 7</g>
</Rot>
<Duty>
    <h>line 8</h>
    <i>line 9</i>
    <j>line 10</j>
    <k>line 11</k>
    <l>line 12</l>
</Duty>
<Leg>
    <m>line 13</m>
    <n>line 14</n>
    <o>line 15</o>
</Leg>
<Leg>
    <m>line 13</m>
    <n>line 14</n>
    <o>line 15</o>
</Leg>
<NFActy>
    <d>line 214</d>
    <e>line 215</e>
    <f>line 216</f>
    <g>line 217</g>
</NFActy>
<Rot>
    <d>line 19</d>
    <e>line 20</e>
    <f>line 21</f>
    <g>line 22</g>
</Rot>
<Duty>
    <h>line 23</h>
    <i>line 24</i>
    <j>line 25</j>
    <k>line 26</k>
    <l>line 27</l>
</Duty>
<Leg>
    <m>line 28</m>
    <n>line 29</n>
    <o>line 30</o>
</Leg>
<Leg>
    <m>line 13</m>
    <n>line 14</n>
    <o>line 15</o>
</Leg>
<NFActy>
    <d>line 224</d>
    <e>line 225</e>
    <f>line 226</f>
    <g>line 227</g>
</NFActy>
</root>

I get the result

<root>
   <root1>1234</root1>
   <root2>5678</root2>
   <Schedule>
      <a>line 1</a>
      <b>line 2</b>
      <c>line 3</c>
      <Rot>
         <d>line 4</d>
         <e>line 5</e>
         <f>line 6</f>
         <g>line 7</g>
         <Duty>
            <SequenceCount>1</SequenceCount>
            <h>line 8</h>
            <i>line 9</i>
            <j>line 10</j>
            <k>line 11</k>
            <l>line 12</l>
            <Leg>
               <SequenceCount>1</SequenceCount>
               <m>line 13</m>
               <n>line 14</n>
               <o>line 15</o>
            </Leg>
            <Leg>
               <SequenceCount>2</SequenceCount>
               <m>line 13</m>
               <n>line 14</n>
               <o>line 15</o>
            </Leg>
         </Duty>
      </Rot>
      <NFActy>
         <SequenceCount>2</SequenceCount>
         <d>line 214</d>
         <e>line 215</e>
         <f>line 216</f>
         <g>line 217</g>
      </NFActy>
      <Rot>
         <d>line 19</d>
         <e>line 20</e>
         <f>line 21</f>
         <g>line 22</g>
         <Duty>
            <SequenceCount>3</SequenceCount>
            <h>line 23</h>
            <i>line 24</i>
            <j>line 25</j>
            <k>line 26</k>
            <l>line 27</l>
            <Leg>
               <SequenceCount>1</SequenceCount>
               <m>line 28</m>
               <n>line 29</n>
               <o>line 30</o>
            </Leg>
            <Leg>
               <SequenceCount>2</SequenceCount>
               <m>line 13</m>
               <n>line 14</n>
               <o>line 15</o>
            </Leg>
         </Duty>
      </Rot>
      <NFActy>
         <SequenceCount>4</SequenceCount>
         <d>line 224</d>
         <e>line 225</e>
         <f>line 226</f>
         <g>line 227</g>
      </NFActy>
   </Schedule>
</root>