将第二个分组应用于组中的所有单个emenet

时间:2018-08-30 03:45:54

标签: xslt-2.0 xslt-grouping

如果要根据键对项目进行分组,则使用for-each-group,如果不属于任何组,则应用第二次分组。

我的示例XML是

<items>
    <item id="123" name="Java">
        <price></price>
        <description></description> 
    </item>

    <item id="123" name="Java and XML">
        <price></price>
        <description></description> 
    </item>

    <item id="234" name="python">
        <price></price>
        <description></description> 
    </item>

    <item id="456" name="scala">
        <price></price>
        <description></description> 
    </item>

    <item id="" name="python">
        <price></price>
        <description></description> 
    </item>


    <item id="768" name="scala">
        <price></price>
        <description></description> 
    </item>

    <item id="891" name="angular">
        <price></price>
        <description></description> 
    </item>
  </items>

首先,我想按id分组,如果分组中有多个元素,则我将组成一个分组,否则我将对name应用另一个分组,然后形成该分组,最后是没有任何分组,然后进行自己的分组。

输出应该是这样的

<items>
    <group>
        <item id="123" name="Java">
            <price></price>
            <description></description> 
        </item>

        <item id="123" name="Java and XML">
            <price></price>
            <description></description> 
        </item>
    </group>
    <group>
        <item id="234" name="python">
            <price></price>
            <description></description> 
        </item>
        <item id="" name="python">
            <price></price>
            <description></description> 
        </item>
    </group>
    <group>
        <item id="456" name="scala">
            <price></price>
            <description></description> 
        </item>
        <item id="768" name="scala">
            <price></price>
            <description></description> 
        </item>
    </group>
    <group>
        <item id="891" name="angular">
            <price></price>
            <description></description> 
        </item>
    </group>
</items>

如何对不在任何组中的分组进行分组?是for-each-group的正确选择。

更新

我尝试过这种方法

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

    <xsl:variable name="nonMatched" as="element()*">
        <xsl:for-each-group select="./items/item" group-by="@id">           
            <xsl:if test="count(current-group())  lt 2">
                <xsl:sequence select="current-group()"/>
            </xsl:if>
        </xsl:for-each-group>
    </xsl:variable>

    <xsl:template match="/">
    <items>
       <xsl:for-each-group select="/items/item" group-by="@id">
            <xsl:if test="count(current-group()) gt 1">
                <group>
                <xsl:copy-of select="current-group()"/>
                </group>
            </xsl:if> 
        </xsl:for-each-group>

    <xsl:for-each-group select="$nonMatched" group-by="@name">
        <group>
        <xsl:copy-of select="current-group()"/>
        </group>
    </xsl:for-each-group>
    </items>
    </xsl:template>

1 个答案:

答案 0 :(得分:1)

一种方法是将项目推入不同的模板,在这些模板中,您可以使用键来标识组和组的条件,然后为每个组中的第一个项目形成组,并为另一个组使用空模板每组中的项目;请注意,以下代码利用了顺序https://www.w3.org/TR/xslt-30/#conflict所赋予的优先级,因此,尽管您也可以使用priority属性强加规则以支持基于@id的规则,但下面的模板顺序很重要。分组,然后基于@name进行分组,然后对其他分组条件未涵盖的任何项目(即match="item")进行分组:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

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

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

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

  <xsl:template match="item">
      <group>
          <xsl:copy-of select="."/>
      </group>
  </xsl:template>

  <xsl:template match="item[key('name', @name)[2] and . is key('name', @name)[1]]">
      <group>
          <xsl:copy-of select="key('name', @name)"/>
      </group>
  </xsl:template>

  <xsl:template match="item[key('name', @name)[2] and not(. is key('name', @name)[1])]"/>

  <xsl:template match="item[key('id', @id)[2] and . is key('id', @id)[1]]">
      <group>
          <xsl:copy-of select="key('id', @id)"/>
      </group>
  </xsl:template>

  <xsl:template match="item[key('id', @id)[2] and not(. is key('id', @id)[1])]"/> 

</xsl:stylesheet>

在线https://xsltfiddle.liberty-development.net/bdxtqt,使用XSLT 3,但您可以使用身份模板替换开始时使用的xsl:mode声明

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

使其与XSLT 2处理器一起使用。唯一需要说明的是,如果存在多个匹配项,而不是采用最后一个匹配模板,则XSLT 2处理器可以诉诸于错误报告,我不记得是哪个XSLT 2处理器执行了该操作,但是如前所述,使用优先级可以解决该问题。 / p>

您发布的方法也应该起作用:

  <xsl:template match="items">
    <xsl:copy>
        <xsl:variable name="nonMatched" as="element()*">
            <xsl:for-each-group select="item" group-by="@id">           
                <xsl:sequence
                  select="if (not(current-group()[2]))
                          then .
                          else ()"/>
            </xsl:for-each-group>
        </xsl:variable>
        <xsl:for-each-group select="item except $nonMatched" group-by="@id">
            <group>
                <xsl:apply-templates select="current-group()"/>
            </group>
        </xsl:for-each-group>
        <xsl:for-each-group select="$nonMatched" group-by="@name">
            <group>
                <xsl:apply-templates select="current-group()"/>
            </group>
        </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

https://xsltfiddle.liberty-development.net/3NzcBtv