XSL键分组元素没有键

时间:2015-11-18 13:36:51

标签: xslt

我想将元素分组到xml文件中(使用XSLT 1.0)。我尝试使用xsl:key函数。如果所有元素都有一个键,它工作正常。但我必须处理没有钥匙的元素。 实际上,没有键的元素被组合在一起。但我需要的是,如果元素没有键,则元素被分组到最后定义的键。

我希望我的代码片段更加明显。

如果第一个元素没有密钥,则应将其分组到第一个定义的密钥。

输入XML

<position>
<item>
    <ZUZ_ID>001</ZUZ_ID>
    <BRUTTO>154.70</BRUTTO>
</item>
<item>
    <ZUZ_ID/>
    <BRUTTO>2.73</BRUTTO>
</item>
<item>
    <ZUZ_ID>002</ZUZ_ID>
    <BRUTTO>17.85</BRUTTO>
</item>
<item>
    <ZUZ_ID>001</ZUZ_ID>
    <BRUTTO>17.85</BRUTTO>
</item>
</position>

实际XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="myGroup" match="item" use="normalize-space(./ZUZ_ID)"/>

  <xsl:template match="position">
    <groups>
         <xsl:for-each select="./item[count(.|key('myGroup', normalize-space(ZUZ_ID))[1]) = 1]">
               <group><xsl:value-of select="(ZUZ_ID)"/></group>
                <xsl:for-each select="key('myGroup',(ZUZ_ID))">
                   <item><xsl:value-of select="(BRUTTO)"/></item>
                </xsl:for-each>
         </xsl:for-each>
    </groups>                               
   </xsl:template>
</xsl:stylesheet>

实际结果XML

<groups>
<group>001</group>
<item>154.70</item>
<item>17.85</item>
<group/>
<item>2.73</item>
<group>002</group>
<item>17.85</item>
</groups>

所需的输出XML

<groups>
<group>001</group>
<item>154.70</item>
<item>17.85</item>
<item>2.73</item>   
<group>002</group>
<item>17.85</item>
</groups>

另一个例子

Item 1 (ZUZID="")
Item 2 (ZUZID="1")
Item 3 (ZUZID="1")
Item 4 (ZUZID="2")
Item 5 (ZUZID="")
Item 6 (ZUZID="")
Item 7 (ZUZID="3")

这应该分组到

Group 1 (Item 1, Item 2, Item 3)
Group 2 (Item 4, Item 5, Item 6)
Group 3 (Item 7)

另一个例子

Item 1 (ZUZID="")
Item 2 (ZUZID="")

这应该分组到一个包含所有第1项和第2项

的组

另一个XML输入示例

<position>
<item>
    <ZUZ_ID/>
    <BRUTTO>10</BRUTTO>
</item>
<item>
    <ZUZ_ID>001</ZUZ_ID>
    <BRUTTO>20</BRUTTO>
</item>
<item>
    <ZUZ_ID>001</ZUZ_ID>
    <BRUTTO>30</BRUTTO>
</item>
<item>
    <ZUZ_ID>002</ZUZ_ID>
    <BRUTTO>40</BRUTTO>
</item>
<item>
    <ZUZ_ID/>
    <BRUTTO>50</BRUTTO>
</item>
</position>

期望输出

<groups>
   <group>001</group>
   <item>10</item>
   <item>20</item>
   <item>30</item>
   <group>002</group>
   <item>40</item>
   <item>50</item>
</groups>

2 个答案:

答案 0 :(得分:1)

你的元素有一个键,它是空字符串,如果你想根据前面的兄弟定义一个键,那么使用

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="myGroup" match="item" use="(ZUZ_ID[normalize-space()] | preceding-sibling::item[normalize-space(ZUZ_ID)]/ZUZ_ID)[last()]"/>

  <xsl:template match="position">
    <groups>
         <xsl:for-each select="./item[count(.|key('myGroup', (ZUZ_ID[normalize-space()] | preceding-sibling::item[normalize-space(ZUZ_ID)]/ZUZ_ID)[last()])[1]) = 1]">
               <group><xsl:value-of select="(ZUZ_ID)"/></group>
                <xsl:for-each select="key('myGroup',(ZUZ_ID))">
                   <item><xsl:value-of select="(BRUTTO)"/></item>
                </xsl:for-each>
         </xsl:for-each>
    </groups>                               
   </xsl:template>
</xsl:stylesheet>

但请注意,输出顺序为

<groups>
   <group>001</group>
   <item>154.70</item>
   <item>2.73</item>
   <item>17.85</item>
   <group>002</group>
   <item>17.85</item>
</groups>

如果您想要处理更多案件,那么您可以为不同案件添加钥匙

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="myGroup"
  match="item[normalize-space(ZUZ_ID)]"
  use="ZUZ_ID"/>

<xsl:key name="myGroup"
  match="item[not(normalize-space(ZUZ_ID)) and preceding-sibling::item[normalize-space(ZUZ_ID)]]"
  use="preceding-sibling::item[normalize-space(ZUZ_ID)][1]/ZUZ_ID"/>

<xsl:key name="myGroup"
  match="item[not(normalize-space(ZUZ_ID)) and not(preceding-sibling::item[normalize-space(ZUZ_ID)])]"
  use="following-sibling::item[normalize-space(ZUZ_ID)][1]/ZUZ_ID"/>

  <xsl:template match="position">
    <groups>
         <xsl:for-each select="./item[count(.|key('myGroup', (ZUZ_ID[normalize-space()] | preceding-sibling::item[normalize-space(ZUZ_ID)]/ZUZ_ID)[last()])[1]) = 1]">
               <group><xsl:value-of select="(ZUZ_ID)"/></group>
                <xsl:for-each select="key('myGroup',(ZUZ_ID))">
                   <item><xsl:value-of select="(BRUTTO)"/></item>
                </xsl:for-each>
         </xsl:for-each>
    </groups>                               
   </xsl:template>
</xsl:stylesheet>

未经测试,因为您没有提供任何XML输入样本。

答案 1 :(得分:1)

我更愿意在两个过程中执行此操作,首先为每个项目配备一个ID。然后分组变得简单。

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="item-by-id" match="item" use="@id"/>

<xsl:template match="/position">
    <!-- first pass  -->
    <xsl:variable name="items">
        <xsl:for-each select="item">
            <item id="{ZUZ_ID[normalize-space()] | preceding-sibling::item[normalize-space(ZUZ_ID)][1][not(normalize-space(current()/ZUZ_ID))]/ZUZ_ID | following-sibling::item[normalize-space(ZUZ_ID)][1][not(normalize-space(current()/ZUZ_ID))]/ZUZ_ID }">
                <xsl:value-of select="BRUTTO" />
            </item>
        </xsl:for-each>
    </xsl:variable> 
    <!-- output  -->
    <groups>
        <xsl:for-each select="exsl:node-set($items)/item[count(. | key('item-by-id', @id)[1]) = 1]">
            <group>
                <xsl:value-of select="@id"/>
            </group>
            <xsl:for-each select="key('item-by-id', @id)">
                <item>
                    <xsl:value-of select="."/>
               </item>
           </xsl:for-each>

        </xsl:for-each>
    </groups>                               
</xsl:template>

</xsl:stylesheet>