更多XSL分组 - 如何删除具有重复子属性的父节点?

时间:2014-10-07 23:34:46

标签: xslt muenchian-grouping

是的,这是另一个XSLT分组/重复问题,但我无法找到任何可以成功应用于我的情况的答案。

这是原始的XML:

<data jsxid="jsxroot">
   <record jsxid="10" groupNum="1319" item="q123"  total="1"/>
   <record jsxid="20" groupNum="1319" item="w123"  total="1"/>
   <record jsxid="30" groupNum="1319" item=""  total="0"/>
   <record jsxid="40" groupNum="1322" item="z123" total="1"/>
   <record jsxid="50" groupNum="1322" item="x123" total="1"/>
   <record jsxid="60" groupNum="1322" item="c123" total="1"/>
   <record jsxid="70" groupNum="1322" item="" total="0"/>
   <record jsxid="80" groupNum="1323" item="x123" total="1"/>
   <record jsxid="90" groupNum="1323" item="c123" total="1"/>
   <record jsxid="100" groupNum="1323" item="z123" total="1"/>
   <record jsxid="110" groupNum="1323" item="" total="0"/>
</data>

首先,我需要按属性分组&#34; groupNum&#34;并包装在父元素jsxid中,该元素随每个组递增,因此输出如下所示:

<data jsxid="jsxroot">
    <record jsxid="1">
        <record jsxid="10" groupNum="1319" item="q123"  total="1"/>
        <record jsxid="20" groupNum="1319" item="w123"  total="1"/>
        <record jsxid="30" groupNum="1319" item=""  total="0"/>
    </record>
    <record jsxid="2">  
        <record jsxid="40" groupNum="1322" item="z123" total="1"/>
        <record jsxid="50" groupNum="1322" item="x123" total="1"/>
        <record jsxid="60" groupNum="1322" item="c123" total="1"/>
        <record jsxid="70" groupNum="1322" item="" total="0"/>
    </record>
    <record jsxid="3">  
        <record jsxid="80" groupNum="1323" item="x123" total="1"/>
        <record jsxid="90" groupNum="1323" item="c123" total="1"/>
        <record jsxid="100" groupNum="1323" item="z123" total="1"/>
        <record jsxid="110" groupNum="1323" item="" total="0"/>
    </record>
</data>

我能够用这个样式表来实现这个目标:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes" />

    <xsl:key name="groupNum" match="data/*" use="@groupNum" />

    <xsl:template match="data">
    <data>
            <xsl:apply-templates select="*[generate-id(.)=generate-id(key('groupNum',@groupNum)[1])]"/>
    </data>
    </xsl:template>


    <xsl:template match="*">
        <record jsxid="{position()}" >
          <xsl:copy-of select="key('groupNum', @groupNum)" />
        </record>
    </xsl:template>

</xsl:stylesheet>

现在我需要删除任何包含与另一个分组完全相同的项目的分组,其中记录总数=&#34; 1&#34;。如果您查看上面的jsxid = 2和jsxid = 3的分组,您会看到它们都包含以下项属性,但顺序不同:

item="x123"
item="c123"
item="z123"

所以我想删除整个jsxid = 3分组并让最终输出看起来像这样:

<data jsxid="jsxroot">
    <record jsxid="1">
        <record jsxid="1" groupNum="1319" item="q123"  total="1"/>
        <record jsxid="2" groupNum="1319" item="w123"  total="1"/>
        <record jsxid="3" groupNum="1319" item=""  total="0"/>
    </record>
    <record jsxid="2">  
        <record jsxid="4" groupNum="1322" item="z123" total="1"/>
        <record jsxid="5" groupNum="1322" item="x123" total="1"/>
        <record jsxid="6" groupNum="1322" item="c123" total="1"/>
        <record jsxid="7" groupNum="1322" item="" total="0"/>
    </record>
</data>

编辑:如果我想将新属性添加为&#34;分组标识符,该怎么办呢?&#34;而不是删除重复的分组。我称它为&#34; groupType&#34;所以最后两个分组相同的上述情况的输出,它们将具有相同的grouptype:

<data jsxid="jsxroot">
    <record jsxid="1">
        <record jsxid="10" groupNum="1319" item="q123"  groupType = "type1" total="1"/>
        <record jsxid="20" groupNum="1319" item="w123"  groupType = "type1" total="1"/>
        <record jsxid="30" groupNum="1319" item=""  groupType = "type1" total="0"/>
    </record>
    <record jsxid="2">  
        <record jsxid="40" groupNum="1322" item="z123" groupType = "type2" total="1"/>
        <record jsxid="50" groupNum="1322" item="x123" groupType = "type2" total="1"/>
        <record jsxid="60" groupNum="1322" item="c123" groupType = "type2" total="1"/>
        <record jsxid="70" groupNum="1322" item="" groupType = "type2" total="0"/>
    </record>
    <record jsxid="3">  
        <record jsxid="80" groupNum="1323" item="x123" groupType = "type2" total="1"/>
        <record jsxid="90" groupNum="1323" item="c123" groupType = "type2" total="1"/>
        <record jsxid="100" groupNum="1323" item="z123" groupType = "type2" total="1"/>
        <record jsxid="110" groupNum="1323" item="" groupType = "type2" total="0"/>
    </record>
</data>

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:0)

这并不简单,所以请坐在座位上:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:variable name="root" select="/" />

<xsl:key name="rec-by-group" match="record" use="@groupNum" />
<xsl:key name="group-by-items" match="group" use="@items" />

<xsl:template match="/">
    <!-- first pass -->
    <xsl:variable name="groups">
        <!-- for each unique groupNum ... -->
        <xsl:for-each select="data/record[count(. | key('rec-by-group', @groupNum)[1]) = 1]">
            <xsl:variable name="groupNum" select="@groupNum" />
            <!-- ... create a group ... -->
            <group groupNum="{$groupNum}">
                <!-- ... and concatenate all its items - in known order(!) - into a single attribute. -->
                <xsl:attribute name="items">
                    <!-- switch context back to document in order to use key -->
                    <xsl:for-each select="$root">
                        <xsl:for-each select="key('rec-by-group', $groupNum)[@total=1]">
                            <xsl:sort select="@item" data-type="text" order="ascending"/>
                            <xsl:value-of select="@item"/>
                            <xsl:text>|</xsl:text>
                        </xsl:for-each>
                    </xsl:for-each>
                </xsl:attribute>
            </group>
        </xsl:for-each>
    </xsl:variable>

    <!-- final pass -->
    <data jsxid="jsxroot">
        <!-- for each unique group (unique by its @items attribute) ... -->
        <xsl:for-each select="exsl:node-set($groups)/group[count(. | key('group-by-items', @items)[1]) = 1]">
            <!-- ... create a wrapper record ... -->
            <record jsxid="{position()}">
                <xsl:variable name="groupNum" select="@groupNum" />
                <!-- switch context back to document in order to use key -->
                <xsl:for-each select="$root">
                    <!-- ... and list the records in this group. -->
                    <xsl:for-each select="key('rec-by-group', $groupNum)">
                        <record>
                            <xsl:attribute name="jsxid">
                                <xsl:number/>
                            </xsl:attribute>
                            <xsl:copy-of select="@groupNum | @item | @total"/>
                        </record>
                    </xsl:for-each>
                </xsl:for-each>
            </record>
        </xsl:for-each>
    </data>
</xsl:template>

</xsl:stylesheet>

编辑:

响应您的修改:

如果您可以使用groupType作为任意(虽然是唯一的)号码,您可以将“最终通行证”部分更改为:

<!-- final pass -->
<data jsxid="jsxroot">
    <!-- for each group ... -->
    <xsl:for-each select="exsl:node-set($groups)/group">
        <!-- ... create a wrapper record ... -->
        <record jsxid="{position()}">
            <xsl:variable name="groupNum" select="@groupNum" />
            <xsl:variable name="groupType" select="key('group-by-items', @items)[1]/@groupNum" />
            <!-- switch context back to document in order to use key -->
            <xsl:for-each select="$root">
                <!-- ... and list the records in this group. -->
                <xsl:for-each select="key('rec-by-group', $groupNum)">
                    <record groupType="{$groupType}">
                        <xsl:attribute name="jsxid">
                            <xsl:number/>
                        </xsl:attribute>
                        <xsl:copy-of select="@groupNum | @item | @total"/>
                    </record>
                </xsl:for-each>
            </xsl:for-each>
        </record>
    </xsl:for-each>
</data>

使用您的输入示例,这将返回:

<?xml version="1.0" encoding="UTF-8"?>
<data jsxid="jsxroot">
   <record jsxid="1">
      <record groupType="1319" jsxid="1" groupNum="1319" item="q123" total="1"/>
      <record groupType="1319" jsxid="2" groupNum="1319" item="w123" total="1"/>
      <record groupType="1319" jsxid="3" groupNum="1319" item="" total="0"/>
   </record>
   <record jsxid="2">
      <record groupType="1322" jsxid="4" groupNum="1322" item="z123" total="1"/>
      <record groupType="1322" jsxid="5" groupNum="1322" item="x123" total="1"/>
      <record groupType="1322" jsxid="6" groupNum="1322" item="c123" total="1"/>
      <record groupType="1322" jsxid="7" groupNum="1322" item="" total="0"/>
   </record>
   <record jsxid="3">
      <record groupType="1322" jsxid="8" groupNum="1323" item="x123" total="1"/>
      <record groupType="1322" jsxid="9" groupNum="1323" item="c123" total="1"/>
      <record groupType="1322" jsxid="10" groupNum="1323" item="z123" total="1"/>
      <record groupType="1322" jsxid="11" groupNum="1323" item="" total="0"/>
   </record>
</data>

如您所见,它使用当前类型的第一组groupNum作为groupType值。或者,您也可以使用该组的自动生成ID:

<xsl:variable name="groupType" select="generate-id(key('group-by-items', @items)[1])" />.

答案 1 :(得分:-1)

要仅复制第一个实例,在生成每个项目时,请使用count(preceding-sibling :: * [])= 0将其括在条件中。

<xsl:template match="record">
    <xsl:variable name="item" select="item"/>
    <record jsxid="{position()}" >
        <xsl:if test="count (preceding-sibling::*[item=$item])=0">
           <xsl:copy-of select="key('groupNum', @groupNum)" />
        </xsl:if>
    </record>
</xsl:template>