使用XLST对XML中的类似节点进行分组

时间:2018-04-17 17:49:09

标签: xml xslt

我找到了一个similar问题,但我无法找到解决问题的方法。

我有一个XML如下

<name>
    <text class="002. AB vs BC">  Sample</text>
    <text class="003. DC vs BC">  Sample</text>
    <text class="004. CD vs BC">  Sample</text>
    <text class="005. AB vs BC">  Sample</text>
    <text class="006. AB vs BC">  Sample</text>
    <text class="007. EF vs BC">  Sample</text>
    <text class="008. CD vs BC">  Sample</text>
    <text class="009. DC vs BC">  Sample</text>
    <text class="010. AB vs BC">  Sample</text>
    <text class="011. EF vs BC">  Sample</text>
    <text class="012. AB vs BC">  Sample</text>

</name>

我需要将类属性中类似第一个单词的所有节点分组如下

<name>
    <group name="AB">
        <text class="002. AB vs BC">  Sample</text>
        <text class="005. AB vs BC">  Sample</text>
        <text class="006. AB vs BC">  Sample</text>
        <text class="010. AB vs BC">  Sample</text>
        <text class="012. AB vs BC">  Sample</text>
    </group>
    <group name="EF">
        <text class="007. EF vs BC">  Sample</text>
        <text class="011. EF vs BC">  Sample</text>
    </group>
    <group name="CD">
        <text class="008. CD vs BC">  Sample</text>
        <text class="004. CD vs BC">  Sample</text>
    </group>
    <group name="DC">
        <text class="003. DC vs BC">  Sample</text>
        <text class="009. DC vs BC">  Sample</text>
    </group>
</name>

如何实现这一目标?

2 个答案:

答案 0 :(得分:1)

这是一个非常简单的分组问题。

如果您仅限于XSLT 1.0,则需要使用Muenchian Grouping

如果您使用的是XSLT 2.0+,则可以使用xsl:for-each-group

...实例

XSLT 1.0

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

  <xsl:key name="class" match="text" 
    use="substring-before(substring-after(normalize-space(@class), ' '),' ')"/>

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

  <xsl:template match="/name">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:for-each select="text[count(.|key('class', substring-before(substring-after(normalize-space(@class), ' '),' '))[1])=1]">
        <xsl:variable name="key" select="substring-before(substring-after(normalize-space(@class), ' '),' ')"/>
        <group name="{$key}">
          <xsl:apply-templates select="key('class',$key)"/>
        </group>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

小提琴:http://xsltfiddle.liberty-development.net/94hvTyR

XSLT 3.0 (如果使用1.0样式表中的标识模板替换xsl:mode,则可以使此有效2.0)

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

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="name">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:for-each-group select="text" 
        group-by="tokenize(normalize-space(@class),'\s+')[2]">
        <group name="{current-grouping-key()}">
          <xsl:apply-templates select="current-group()"/>
        </group>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

小提琴:http://xsltfiddle.liberty-development.net/94hvTyR/1

注意:输出与您的示例的顺序不同,但我没有看到任何订购逻辑。

答案 1 :(得分:1)

使用XSLT-2.0,您可以将xsl:for-each-group与这些模板一起使用:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="text()" />

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

  <xsl:template match="name">
    <xsl:copy>
        <xsl:for-each-group select="text" group-by="tokenize(@class,' ')[2]">
            <group name="{current-grouping-key()}">
                <xsl:for-each select="current-group()">
                    <xsl:copy-of select="." />
                </xsl:for-each>
            </group>
        </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

输出为:

<name>
   <group name="AB">
      <text class="002. AB vs BC">  Sample</text>
      <text class="005. AB vs BC">  Sample</text>
      <text class="006. AB vs BC">  Sample</text>
      <text class="010. AB vs BC">  Sample</text>
      <text class="012. AB vs BC">  Sample</text>
   </group>
   <group name="DC">
      <text class="003. DC vs BC">  Sample</text>
      <text class="009. DC vs BC">  Sample</text>
   </group>
   <group name="CD">
      <text class="004. CD vs BC">  Sample</text>
      <text class="008. CD vs BC">  Sample</text>
   </group>
   <group name="EF">
      <text class="007. EF vs BC">  Sample</text>
      <text class="011. EF vs BC">  Sample</text>
   </group>
</name>