分组数据的分组

时间:2014-12-04 13:38:01

标签: xslt xslt-1.0 xslt-grouping exslt

输入:

<persons>
    <person name="John" role="Writer"/>
    <person name="John" role="Poet"/>
    <person name="Jacob" role="Writer"/>
    <person name="Jacob" role="Poet"/>
    <person name="Joe" role="Poet"/>
</persons>

预期输出:

<groups>
    <group roles="Wriet, Poet" persons="John, Jacob"/>
    <group roles="Poet" persons="Joe"/>
</groups>

如上例所示,我首先需要对人名进行分组并找到每个人的角色。如果发现不止一个人具有相同的角色集(例如John和Jacob都是作家和诗人),那么我需要对每组角色进行分组并列出人名。

我可以使用Muenchian方法或EXSLT set:distinct等进行第一级分组。

<groups>
    <group roles="Wriet, Poet" persons="John"/>
    <group roles="Wriet, Poet" persons="Jacob"/>
    <group roles="Poet" persons="Joe"/>
</groups>

使用XSLT 1.0和EXSLT转换上述内容:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sets="http://exslt.org/sets" extension-element-prefixes="sets">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:key name="persons-by-name" match="person" use="@name"/>
    <xsl:template match="persons">
        <groups>
            <xsl:for-each select="sets:distinct(person/@name)">
                <group>
                    <xsl:attribute name="persons"><xsl:value-of select="."/></xsl:attribute>
                    <xsl:attribute name="roles">
                        <xsl:for-each select="key('persons-by-name', .)">
                            <xsl:value-of select="@role"/>
                            <xsl:if test="position()!=last()"><xsl:text>, </xsl:text></xsl:if>
                        </xsl:for-each>
                    </xsl:attribute>
                </group>
            </xsl:for-each>
        </groups>
    </xsl:template>
</xsl:stylesheet>

但是,我需要帮助来了解如何对分组角色进行分组。

如果没有XSLT 1.0解决方案,请随时推荐XSLT 2.0方法。

2 个答案:

答案 0 :(得分:1)

我做了与你已经做过的完全相同的事情,然后又向前迈了一步又重新组合了。现在我输入以下输出:

<?xml version="1.0" encoding="UTF-8"?>
<groups>
    <group roles="Writer,Poet" persons="John,Jacob"/>
    <group roles="Poet" persons="Joe"/>
</groups>

这是XSLT 2.0

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns="" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:avintis="http://www.avintis.com/esb" exclude-result-prefixes="#all" version="2.0">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/persons">
        <groups>
            <xsl:variable name="persons" select="."/>

            <!-- create a temporary variable containing all roles of a person -->
            <xsl:variable name="roles">
                <xsl:for-each select="distinct-values(person/@name)">
                    <xsl:sort select="."/>
                    <xsl:variable name="name" select="."/>
                    <xsl:element name="group">
                        <xsl:attribute name="roles">
                            <!-- sort the roles of each person -->
                            <xsl:variable name="rolesSorted">
                                <xsl:for-each select="$persons/person[@name=$name]">
                                    <xsl:sort select="@role"/>
                                    <xsl:copy-of select="."/>
                                </xsl:for-each>
                            </xsl:variable>
                            <xsl:value-of select="string-join($rolesSorted/person/@role,',')"/>
                        </xsl:attribute>
                        <xsl:attribute name="persons" select="."/>
                    </xsl:element>
                </xsl:for-each>
            </xsl:variable>

            <!-- now loop again over all roles of the persons and group persons having the same roles -->
            <xsl:for-each select="distinct-values($roles/group/@roles)">
                <xsl:element name="group">
                    <xsl:variable name="name" select="."/>
                    <xsl:attribute name="roles" select="$name"/>
                    <xsl:attribute name="persons">
                        <xsl:value-of select="string-join($roles/group[@roles=$name]/@persons,',')"/>
                    </xsl:attribute>
                </xsl:element>
            </xsl:for-each>
        </groups>
    </xsl:template>

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

</xsl:stylesheet>

角色也会被排序 - 因此独立于角色和人员的输入顺序。

答案 1 :(得分:1)

以这种方式试试吗?

XSLT 1.0
(使用EXSLT node-set()和distinct()函数)

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

<xsl:key name="person-by-name" match="person" use="@name" />
<xsl:key name="person-by-roles" match="person" use="@roles" />

<xsl:variable name="distinct-persons">
    <xsl:for-each select="set:distinct(/persons/person/@name)">
        <person name="{.}">
            <xsl:attribute name="roles">
                <xsl:for-each select="key('person-by-name', .)/@role">
                    <xsl:sort/>
                    <xsl:value-of select="." />
                    <xsl:if test="position()!=last()">
                        <xsl:text>, </xsl:text>
                    </xsl:if>
                </xsl:for-each>
            </xsl:attribute>
        </person>
    </xsl:for-each> 
</xsl:variable>

<xsl:template match="/">
    <groups>
        <xsl:for-each select="set:distinct(exsl:node-set($distinct-persons)/person/@roles)">
            <group roles="{.}">
                <xsl:attribute name="names">
                    <xsl:for-each select="key('person-by-roles', .)/@name">
                        <xsl:value-of select="." />
                        <xsl:if test="position()!=last()">
                            <xsl:text>, </xsl:text>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:attribute>
            </group>
        </xsl:for-each>
    </groups>
</xsl:template>


</xsl:stylesheet>

<强>结果

<?xml version="1.0" encoding="UTF-8"?>
<groups>
   <group roles="Poet, Writer" names="John, Jacob"/>
   <group roles="Poet" names="Joe"/>
</groups>