xsl:for-each-groups位置分组

时间:2013-04-30 16:10:37

标签: xpath xslt-2.0

我有一个需要转换为分层的平面xml文件。我使用了xsl:for-each-group help needed的嵌套分组思想。它除了几个问题外大部分都在工作:

1)元素root1和root2没有显示。

2)元素level21的位置不正确。 sequencecount = 2的第一个level21应该在2个level2元素之间。

非常感谢任何帮助。

感谢。

源XML

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

必填结果XML

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <root1>1234</root1>
  <root2>5678</root2>
  <level1>
    <a>line 1</a>
    <b>line 2</b>
    <c>line 3</c>
    <level2>
      <d>line 4</d>
      <e>line 5</e>
      <f>line 6</f>
      <g>line 7</g>
      <level3>
        <SequenceCount>1</SequenceCount>
        <h>line 8</h>
        <i>line 9</i>
        <j>line 10</j>
        <k>line 11</k>
        <l>line 12</l>
        <level4>
          <SequenceCount>1</SequenceCount>
          <m>line 13</m>
          <n>line 14</n>
          <o>line 15</o>
        </level4>
        <level4>
          <SequenceCount>2</SequenceCount>
          <m>line 13</m>
          <n>line 14</n>
          <o>line 15</o>
        </level4>
      </level3>
    </level2>
    <level21>
          <SequenceCount>2</SequenceCount>
          <d>line 214</d>
          <e>line 215</e>
          <f>line 216</f>
          <g>line 217</g>
    </level21>
    <level2>
      <d>line 19</d>
      <e>line 20</e>
      <f>line 21</f>
      <g>line 22</g>
      <level3>
        <SequenceCount>3</SequenceCount>
        <h>line 23</h>
        <i>line 24</i>
        <j>line 25</j>
        <k>line 26</k>
        <l>line 27</l>
        <level4>
          <SequenceCount>1</SequenceCount>
          <m>line 28</m>
          <n>line 29</n>
          <o>line 30</o>
        </level4>
        <level4>
          <SequenceCount>2</SequenceCount>
          <m>line 13</m>
          <n>line 14</n>
          <o>line 15</o>
        </level4>
      </level3>
    </level2>    
    <level21>
      <SequenceCount>4</SequenceCount>
      <d>line 224</d>
      <e>line 225</e>
      <f>line 226</f>
      <g>line 227</g>
    </level21>
  </level1>
</root>

我的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(.)='level1']">
                    <xsl:choose>
                        <xsl:when test="name(.)='level1'">
                            <level1>
                                <xsl:apply-templates />
                                <xsl:for-each-group select="current-group()" group-starting-with="*[name(.)='level2']">
                                    <xsl:choose>
                                        <xsl:when test="name(.)='level2'">
                                            <level2>
                                                <xsl:apply-templates />
                                                <xsl:for-each-group select="current-group()" group-starting-with="*[name(.)='level3']">
                                                    <xsl:choose>
                                                        <xsl:when test="name(.)='level3'">
                                                            <level3>
                                                                <xsl:element name="SequenceCount"><xsl:number count="level3|level21" level="any"/></xsl:element>
                                                                <xsl:apply-templates />
                                                                <xsl:for-each-group select="current-group()" group-starting-with="*[name(.)='level4']">
                                                                    <xsl:choose>
                                                                        <xsl:when test="name(.)='level4'">
                                                                            <level4>
                                                                                <xsl:element name="SequenceCount"><xsl:number value="position() - 1"/></xsl:element>
                                                                                <xsl:apply-templates />
                                                                            </level4>
                                                                        </xsl:when>
                                                                    </xsl:choose>
                                                                </xsl:for-each-group>
                                                            </level3>
                                                        </xsl:when>
                                                    </xsl:choose>
                                                </xsl:for-each-group>
                                            </level2>
                                        </xsl:when>
                                    </xsl:choose>
                                </xsl:for-each-group>
                                <xsl:for-each-group select="current-group()" group-starting-with="*[name(.)='level21']">
                                    <xsl:choose>
                                        <xsl:when test="name(.)='level21'">
                                            <level21>
                                                <xsl:element name="SequenceCount"><xsl:number count="level3|level21" level="any"/></xsl:element>
                                                <xsl:apply-templates />
                                            </level21>
                                        </xsl:when>
                                    </xsl:choose>
                                </xsl:for-each-group>
                            </level1>
                        </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>

当前输出XML

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <level1>
    <a>line 1</a>
    <b>line 2</b>
    <c>line 3</c>
    <level2>
      <d>line 4</d>
      <e>line 5</e>
      <f>line 6</f>
      <g>line 7</g>
      <level3>
        <SequenceCount>1</SequenceCount>
        <h>line 8</h>
        <i>line 9</i>
        <j>line 10</j>
        <k>line 11</k>
        <l>line 12</l>
        <level4>
          <SequenceCount>1</SequenceCount>
          <m>line 13</m>
          <n>line 14</n>
          <o>line 15</o>
        </level4>
        <level4>
          <SequenceCount>2</SequenceCount>
          <m>line 13</m>
          <n>line 14</n>
          <o>line 15</o>
        </level4>
      </level3>
    </level2>
    <level2>
      <d>line 19</d>
      <e>line 20</e>
      <f>line 21</f>
      <g>line 22</g>
      <level3>
        <SequenceCount>3</SequenceCount>
        <h>line 23</h>
        <i>line 24</i>
        <j>line 25</j>
        <k>line 26</k>
        <l>line 27</l>
        <level4>
          <SequenceCount>1</SequenceCount>
          <m>line 28</m>
          <n>line 29</n>
          <o>line 30</o>
        </level4>
        <level4>
          <SequenceCount>2</SequenceCount>
          <m>line 13</m>
          <n>line 14</n>
          <o>line 15</o>
        </level4>
      </level3>
    </level2>
    <level21>
      <SequenceCount>2</SequenceCount>
      <d>line 214</d>
      <e>line 215</e>
      <f>line 216</f>
      <g>line 217</g>
    </level21>
    <level21>
      <SequenceCount>4</SequenceCount>
      <d>line 224</d>
      <e>line 225</e>
      <f>line 226</f>
      <g>line 227</g>
    </level21>
  </level1>
</root>

1 个答案:

答案 0 :(得分:0)

我认为以下样式表为您提供了所需的分组:

<xsl:stylesheet version="2.0" 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">

<xsl:output 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="*[starts-with(local-name(), concat('level', $level))]">
    <xsl:choose>
      <xsl:when test="self::*[starts-with(local-name(), concat('level', $level))]">
        <xsl:copy>
          <xsl:if test="$level = (3, 4)">
            <SequenceCount><xsl:value-of select="position()"/></SequenceCount>
          </xsl:if>
          <xsl:apply-templates/>
          <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
        </xsl:copy>
      </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="root">
  <xsl:copy>
    <xsl:sequence select="mf:group(*, 1)"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

对于SequenceCount元素,某些值与发布的结果示例中的值相同,有些值不同,有些值丢失。让我们首先确定分组是否符合您的要求,然后我们可以尝试改进序列计数值。您可能希望以书面形式解释计数所指的内容。

[edit]根据您的XSLT代码,我添加了代码来计算不同级别的SequenceNumber,现在完整的样式表

<xsl:stylesheet version="2.0" 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">

<xsl:output 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="*[starts-with(local-name(), concat('level', $level))]">
    <xsl:choose>
      <xsl:when test="self::*[starts-with(local-name(), concat('level', $level))]">
        <xsl:copy>
          <xsl:choose>
            <xsl:when test="self::level3">
              <SequenceCount><xsl:number count="level3 | level21"/></SequenceCount>
            </xsl:when>
            <xsl:when test="self::level4">
              <SequenceCount><xsl:value-of select="position()"/></SequenceCount>
            </xsl:when>
            <xsl:when test="self::level21">
              <SequenceCount><xsl:number count="level3|level21"/></SequenceCount>
            </xsl:when>
          </xsl:choose>
          <xsl:apply-templates/>
          <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
        </xsl:copy>
      </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="root">
  <xsl:copy>
    <xsl:sequence select="mf:group(*, 1)"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

使用Saxon作为输入

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

我得到了结果

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