2015-11-05 84 views
0

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

这里有一个工作示例:

<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> 

哪个正确组 “一” 项目一起,留下“c”项目分开。 然而,如果 “A” 的数据项都进一步嵌套的,它们getted跌出结果:

<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> 

(如果多个条目'c'和'x1'都存在,它们也可以分组,但这不是必需的)

+0

需要说明和解释而导致你想为你的最后一个输入样本,这是我不清楚。 –

+0

添加了所需的结果xml – mjoen

+0

所以,你想保留层次结构,并基本上在每个“组”中包含“op”元素。如果有'op'元素带有'item''c,然后是嵌套组,然后一些'op'元素带有'item''c',会发生什么?你想把所有'c'项目组合在一起,基本上拉起后面的元素吗?或者,如果在原始XML中相邻,那么你是否只想将'op'元素与相同的'item'分组? –

回答

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 s到确保两个<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> 
+0

这适用于我所有的测试用例,除非某些项目具有多个名称元素(我没有包括该示例,对不起)。当存在多个名称时,它会在string()调用中出错。如果所有名称都匹配,它应该只合并项目。 – mjoen

+0

您是否可以编辑问题并显示多个名称的样本和相应的结果?这些名称是分类还是可排序的?我正在考虑使用例如'group-by =“string-join(item/name,'|')”'这只有在名字被排序或者至少可以被排序以获得一种指纹来将祖先分组时才起作用。 –