xslt在多个嵌套级别按名称对项目进行分组

时间:2015-11-05 14:53:34

标签: xslt

我试图让分组优化与xslt一起使用。优化应该将具有相同名称的项目收集到单个items元素中。当项目处于同一级别时,我可以使用它,但是一些输入将它们嵌套在任意级别,我无法弄清楚如何在这些情况下使分组工作。

这是一个有效的例子:

<process>
<op>
    <group>
        <op>
            <item>
                <name>a</name>
                <detail><value>1</value></detail>
            </item>
        </op>
        <op>
            <item>
                <name>a</name>
                <detail><value>2</value></detail>
            </item>
        </op>
        <op>
            <item>
                <name>c</name>
                <name>x1</name>
                <detail><value>3</value></detail>
            </item>
        </op>
    </group>
</op>

这是我的示例转换定义:

<xs:template match="group[op[item[detail]]]">
    <xs:copy>
        <xs:for-each-group select="op" group-by="item/name">
            <op><item>
            <xs:copy-of select="item/name"/>
            <xs:choose>
            <xs:when test="count(current-group()) &gt; 1">
            <details>
            <xs:for-each select="current-group()">
                <detail><value>
                <xs:value-of select="item/detail/value"/>
                </value></detail>
            </xs:for-each>
            </details>
            </xs:when>
            <xs:otherwise>
                <detail><value>
                <xs:value-of select="item/detail/value"/>
                </value></detail>
            </xs:otherwise>
            </xs:choose>
            </item></op>
        </xs:for-each-group>
    </xs:copy>
</xs:template>

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

正确分组&#34; a&#34;项目在一起,离开&#34; c&#34;项目分开。 但是,如果&#34; a&#34;项目进一步嵌套,它们从结果中退出:

<process>
<op>
    <group>
        <op>
            <group>
                <op>
                    <item>
                        <name>a</name>
                        <detail><value>1</value></detail>
                    </item>
                </op>
                <op>
                    <item>
                        <name>a</name>
                        <detail><value>2</value></detail>
                    </item>
                </op>
            </group>
        </op>
        <op>
            <item>
                <name>c</name>
                <name>x1</name>
                <detail><value>3</value></detail>
            </item>
        </op>
    </group>
</op>

所需的结果XML将是:

<process>
<op>
    <group>
        <op>
            <group>
                <op>
                    <item>
                        <name>a</name>
                        <details>
                            <detail><value>1</value></detail>
                            <detail><value>2</value></detail>
                        </details>
                    </item>
                </op>
            </group>
        </op>
        <op>
            <item>
                <name>c</name>
                <name>x1</name>
                <detail><value>3</value></detail>
            </item>
        </op>
    </group>
</op></process>

(如果存在多个带​​有&#39; c&#39;和&#39; x1&#39;的条目,它们也可以分组,但这不是必需的)

1 个答案:

答案 0 :(得分:0)

您需要对所有元素进行分组,并确保那些没有密钥的元素以递归方式处理,这是尝试这样做的一次:

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

<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>

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

<xsl:template match="group[op[item[detail]]]">
    <xsl:copy>
        <xsl:for-each-group select="*" group-by="string(item/name)">
          <xsl:choose>
            <xsl:when test="current-grouping-key() eq ''">
              <xsl:apply-templates select="current-group()"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:copy>
                <item>
                  <xsl:copy-of select="item/name"/>
                  <xsl:choose>
                    <xsl:when test="current-group()[2]">
                      <details>
                        <xsl:copy-of select="current-group()/item/detail"/>
                      </details>
                    </xsl:when>
                    <xsl:otherwise>
                       <xsl:copy-of select="item/detail"/>
                    </xsl:otherwise>
                  </xsl:choose>
                </item>
              </xsl:copy>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each-group>
   </xsl:copy>
</xsl:template>

</xsl:stylesheet>

如果可以有多个name个元素构成分组键,那么使用XSLT 2.0,一种方法可以将string-join(item/name, '|')计算为关键字(条|应该被替换为适当的分隔符,确保它不会出现在name s中。以下是首先对name进行排序以确保<name>a</name><name>b</name><name>b</name><name>a</name>分组的示例:

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

<xsl:param name="separator" as="xs:string" select="'|'"/>

<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>

<xsl:function name="mf:sort" as="xs:string*">
  <xsl:param name="input"/>
  <xsl:perform-sort select="$input">
    <xsl:sort select="."/>
  </xsl:perform-sort>
</xsl:function>

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

<xsl:template match="group[op[item[detail]]]">
    <xsl:copy>
        <xsl:for-each-group select="*" group-by="string-join(mf:sort(item/name), $separator)">
          <xsl:choose>
            <xsl:when test="current-grouping-key() eq ''">
              <xsl:apply-templates select="current-group()"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:copy>
                <item>
                  <xsl:copy-of select="item/name"/>
                  <xsl:choose>
                    <xsl:when test="current-group()[2]">
                      <details>
                        <xsl:copy-of select="current-group()/item/detail"/>
                      </details>
                    </xsl:when>
                    <xsl:otherwise>
                       <xsl:copy-of select="item/detail"/>
                    </xsl:otherwise>
                  </xsl:choose>
                </item>
              </xsl:copy>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each-group>
   </xsl:copy>
</xsl:template>

</xsl:stylesheet>