使用XSLT对数据进行分组并不完全正常

时间:2014-12-17 20:44:17

标签: xml xslt

我创建了一个XSLT,用于为鞋类目录分组数据。鞋子需要按“线”和“品牌”分组。行和品牌标题需要出现在每个部分的开头。每条线都有不止一个品牌。每个品牌通常都有不止一双鞋。

以下是一些示例XML数据:

<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<shoe>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
    <brand>WALKING</brand>
    <style-name>NEW BALANCE-NB 475V2 (WIDE)</style-name>
    <color>Black</color>
    <price>67.99</price>
</shoe>
<shoe>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
    <brand>WALKING</brand>
    <style-name>NEW BALANCE-496 (WIDE)</style-name>
    <color>Grey/Pink</color>
    <price>64.99</price>
</shoe>
<shoe>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
    <brand>CROSS TRANING</brand>
    <style-name>FILA-MEMORY PANACHE</style-name>
    <color>Black/Pink</color>
    <price>69.99</price>
</shoe>
<shoe>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
    <brand>RUNNING</brand>
    <style-name>FILA-VITALITY 2 TRAIL</style-name>
    <color>Grey/Prpl/Turq</color>
    <price>59.99</price>
</shoe>
<shoe>
    <line>LINE 87: MENS BRANDED ATHLETIC</line>
    <brand>CASUAL</brand>
    <style-name>LEVI'S-HAMILTON BUCK HI</style-name>
    <color>Black/Black</color>
    <price>34.99</price>
</shoe>
<shoe>
    <line>LINE 87: MENS BRANDED ATHLETIC</line>
    <brand>CASUAL</brand>
    <style-name>EVERLAST-EVAN SKATE</style-name>
    <color>Black</color>
    <price>29.99</price>
</shoe>
<shoe>
    <line>LINE 87: MENS BRANDED ATHLETIC</line>
    <brand>RUNNING</brand>
    <style-name>SKECHERS-POWER SWITCH (WIDE)</style-name>
    <color>Black/White</color>
    <price>69.99</price>
</shoe>
<shoe>
    <line>LINE 87: MENS BRANDED ATHLETIC</line>
    <brand>RUNNING</brand>
    <style-name>SKECHERS-EQUALIZER GEL TOP </style-name>
    <color>Black</color>
    <price>69.99</price>
</shoe>
</catalog>

这是我的XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8"/>

    <xsl:key name="shoes-by-line" match="shoe" use="line" />
    <xsl:key name="shoes-by-brand" match="shoe" use="concat(line,'#',brand )" />

    <xsl:template match="catalog">
        <catalog>
            <shoes>
                <xsl:for-each select="shoe[count(. | key('shoes-by-line', line)[1]) = 1]">
                    <line>
                        <xsl:value-of select="line"/>
                    </line>
                    <brands>
                        <brand>
                            <xsl:value-of select="brand"/>
                        </brand>
                        <xsl:for-each select="key( 'shoes-by-brand', concat(line,'#',brand ))">
                            <shoe>
                                <style-name>
                                    <xsl:value-of select="style-name"/>
                                </style-name>
                                <color>
                                    <xsl:value-of select="color"/>
                                </color>
                                <price>
                                    <xsl:value-of select="price"/>
                                </price>
                            </shoe>
                        </xsl:for-each>
                    </brands>
                </xsl:for-each>
            </shoes>
        </catalog>
    </xsl:template>

</xsl:stylesheet>

我得到1“线”和1“品牌”,但我没有在每一行获得额外的品牌。每行应至少有两到三个品牌。在这个XML中有2行。

这是我想要的XML结构:

<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<lines>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
<brands>
    <brand>WALKING</brand>
<shoes>
<shoe>
    <style-name>NEW BALANCE-NB 475V2 (WIDE)</style-name>
    <color>Black</color>
    <price>67.99</price>
</shoe>
<shoe>
    <style-name>NEW BALANCE-496 (WIDE)</style-name>
    <color>Grey/Pink</color>
    <price>64.99</price>
</shoe>
</shoes>
</brands>
</lines>
<lines>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
<brands>
    <brand>CROSS TRANING</brand>
<shoes>
<shoe>
    <style-name>FILA-MEMORY PANACHE</style-name>
    <color>Black/Pink</color>
    <price>69.99</price>
</shoe>
<shoe>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
    <brand>RUNNING</brand>
    <style-name>FILA-VITALITY 2 TRAIL</style-name>
    <color>Grey/Prpl/Turq</color>
    <price>59.99</price>
</shoe>
</shoes>
</brands>
</lines>
<lines>
    <line>LINE 87: MENS BRANDED ATHLETIC</line>
<brands>
    <brand>CASUAL</brand>
<shoes>
<shoe>
    <style-name>LEVI'S-HAMILTON BUCK HI</style-name>
    <color>Black/Black</color>
    <price>34.99</price>
</shoe>
<shoe>
    <style-name>EVERLAST-EVAN SKATE</style-name>
    <color>Black</color>
    <price>29.99</price>
</shoe>
</shoes>
</brands>
</lines>
<lines>
  <line>LINE 87: MENS BRANDED ATHLETIC</line>
<brands>
  <brand>RUNNING</brand>
<shoes>
<shoe>
    <line>LINE 87: MENS BRANDED ATHLETIC</line>
<brands>
    <brand>RUNNING</brand>
<shoes>
<shoe>
    <style-name>SKECHERS-POWER SWITCH (WIDE)</style-name>
    <color>Black/White</color>
    <price>69.99</price>
</shoe>
<shoe>
    <style-name>SKECHERS-EQUALIZER GEL TOP </style-name>
    <color>Black</color>
    <price>69.99</price>
</shoe>
</shoes>
</brands>
</lines>
</catalog>

2 个答案:

答案 0 :(得分:1)

通过对XSLT进行一些调整,可以生成所需的结果:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8"/>
  <xsl:key name="shoes-by-line" match="shoe" use="line" />
  <xsl:key name="shoes-by-brand" match="shoe" use="concat(line,'#',brand )" />
  <xsl:template match="catalog">
    <catalog>
        <xsl:for-each select="shoe[count(. | key('shoes-by-line', line)[1]) = 1]">
            <lines>
                <line>
                    <xsl:value-of select="line"/>
                </line>
                <brands>
                    <brand>
                        <xsl:value-of select="brand"/>
                    </brand>
                    <shoes>
                        <xsl:for-each select="key( 'shoes-by-brand', concat(line,'#',brand ))">
                            <shoe>
                                <style-name>
                                    <xsl:value-of select="style-name"/>
                                </style-name>
                                <color>
                                    <xsl:value-of select="color"/>
                                </color>
                                <price>
                                    <xsl:value-of select="price"/>
                                </price>
                            </shoe>
                        </xsl:for-each>
                    </shoes>
                </brands>
            </lines>
        </xsl:for-each>
    </catalog>
  </xsl:template>
</xsl:stylesheet>

您的XSLT已经生成了所需的输出,只需要将一些分组元素移动到不同的位置:

之前:

<catalog>
    <shoes>
       <xsl:for-each select="shoe[count(. | key('shoes-by-line', line)[1]) = 1]">
          <line>
             <xsl:value-of select="line"/>
          </line>
          <brands>
             <brand>
                <xsl:value-of select="brand"/>
             </brand>
                <xsl:for-each select="key('shoes-by-brand', 
                                      concat(line,'#',brand ))">
                   <shoe>
                        ...

调整后:

<catalog>
    <xsl:for-each select="shoe[count(. | key('shoes-by-line', line)[1]) = 1]">
        <lines>
            <line>
                <xsl:value-of select="line"/>
            </line>
            <brands>
                <brand>
                    <xsl:value-of select="brand"/>
                </brand>
                <shoes>
                    <xsl:for-each select="key('shoes-by-brand', 
                                          concat(line,'#',brand ))">
                        <shoe>
                           ...

更新:正如评论中所注意到的,XSLT上面没有提供正确的输出。就像一个不同的方法:使用xsl:for-each-group而不是键来跟随XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8"/>
  <xsl:template match="catalog">
    <catalog>
        <xsl:for-each-group select="shoe" group-by="line">
            <xsl:for-each-group select="current-group()" group-by="brand">
                <lines>
                    <line>
                        <xsl:value-of select="line"></xsl:value-of>
                    </line>
                    <brands>
                        <xsl:for-each-group select="current-group()" group-by="brand">
                            <brand>
                                <xsl:value-of select="current-grouping-key()"></xsl:value-of>
                            </brand>
                            <shoes>
                                <xsl:for-each select="current-group()">
                                    <shoe>
                                        <style-name>
                                            <xsl:value-of select="style-name"/>
                                        </style-name>
                                        <color>
                                            <xsl:value-of select="color"/>
                                        </color>
                                        <price>
                                            <xsl:value-of select="price"/>
                                        </price>
                                    </shoe>
                                </xsl:for-each>
                            </shoes>
                        </xsl:for-each-group>
                    </brands>
                </lines>
            </xsl:for-each-group>
        </xsl:for-each-group>
    </catalog>
  </xsl:template>
</xsl:stylesheet>

当应用于您的输入时,XML会产生以下输出:

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <lines>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
    <brands>
        <brand>WALKING</brand>
        <shoes>
            <shoe>
                <style-name>NEW BALANCE-NB 475V2 (WIDE)</style-name>
                <color>Black</color>
                <price>67.99</price>
            </shoe>
            <shoe>
                <style-name>NEW BALANCE-496 (WIDE)</style-name>
                <color>Grey/Pink</color>
                <price>64.99</price>
            </shoe>
        </shoes>
    </brands>
  </lines>
  <lines>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
    <brands>
        <brand>CROSS TRANING</brand>
        <shoes>
            <shoe>
                <style-name>FILA-MEMORY PANACHE</style-name>
                <color>Black/Pink</color>
                <price>69.99</price>
            </shoe>
        </shoes>
    </brands>
  </lines>
  <lines>
    <line>LINE 43: WOMENS BRANDED ATHLETIC</line>
    <brands>
        <brand>RUNNING</brand>
        <shoes>
            <shoe>
                <style-name>FILA-VITALITY 2 TRAIL</style-name>
                <color>Grey/Prpl/Turq</color>
                <price>59.99</price>
            </shoe>
        </shoes>
    </brands>
  </lines>
  <lines>
    <line>LINE 87: MENS BRANDED ATHLETIC</line>
    <brands>
        <brand>CASUAL</brand>
        <shoes>
            <shoe>
                <style-name>LEVI'S-HAMILTON BUCK HI</style-name>
                <color>Black/Black</color>
                <price>34.99</price>
            </shoe>
            <shoe>
                <style-name>EVERLAST-EVAN SKATE</style-name>
                <color>Black</color>
                <price>29.99</price>
            </shoe>
        </shoes>
    </brands>
  </lines>
  <lines>
    <line>LINE 87: MENS BRANDED ATHLETIC</line>
    <brands>
        <brand>RUNNING</brand>
        <shoes>
            <shoe>
                <style-name>SKECHERS-POWER SWITCH (WIDE)</style-name>
                <color>Black/White</color>
                <price>69.99</price>
            </shoe>
            <shoe>
                <style-name>SKECHERS-EQUALIZER GEL TOP </style-name>
                <color>Black</color>
                <price>69.99</price>
            </shoe>
        </shoes>
    </brands>
  </lines>
</catalog>

请注意,与OP中提供的请求输出存在一个差异 - 行RUNNING的品牌LINE 43: WOMENS BRANDED ATHLETIC是一个单独的品牌,而不是CROSS TRAINING品牌,但也许这是想要的输出中的一个小错误。否则,有必要找到一个解决方案,以便在同一品牌中列出RUNNINGCROSS TRAINING

由于应为每一行的每个品牌列出鞋子,<xsl:for-each-group select="shoe" group-by="line">首先选择按行分组的所有鞋子。然后,使用<xsl:for-each-group select="current-group()" group-by="brand">按品牌选择此组中的所有品牌,并在循环<xsl:for-each select="current-group()">中处理品牌的所有鞋子。

更新:正如评论中所提到的,这是一个XSLT 2.0样式表。我刚刚更正了上面模板中的版本。虽然我已经使用在线XSLT测试工具测试了模板,并将其声明为版本1.0,但它没有抛出任何错误(尽管它应该有)。

答案 1 :(得分:0)

我重新访问了XSLT我相信我找到了使用XSLT 1.0的解决方案

<?xml version="1.0" encoding="UTF-8"?><!-- DWXMLSource="D76-shoebook.xml" -->
 <!DOCTYPE xsl:stylesheet>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" encoding="UTF-8"/>

 <xsl:key name="shoes-by-line" match="shoe" use="line"/>
 <xsl:key name="shoes-by-brand" match="shoe" use="concat(line, '|', brand)"/>
 <xsl:template match="catalog">

 <catalog>
 <xsl:for-each select="shoe[generate-id() = generate-id(key('shoes-by-line', line)[1])]">
 <lines>
 <xsl:for-each select="key('shoes-by-line', line)[generate-id() = generate-id(key('shoes-by-brand', concat(line, '|', brand))[1])]">
 <line><xsl:value-of select="line"/></line><xsl:text>
 </xsl:text><brand><xsl:value-of select="brand"/></brand><xsl:text>
 </xsl:text><shoes>
 <xsl:for-each select="key('shoes-by-brand', concat(line, '|', brand))">
 <shoe>
 <style-name><xsl:value-of select="style-name" /></style-name>
 <color><xsl:value-of select="color" /></color><xsl:text>
 <price><xsl:value-of select="price" /></price><xsl:text>
 </xsl:text>                
 </shoe>        
            </xsl:for-each>
         </shoes>   
      </xsl:for-each>
</lines>
</xsl:for-each>
</catalog>
</xsl:template>
</xsl:stylesheet>

这会从源XML键入LINE然后在BRAND上生成正确的分组和嵌套。