如何在XSLT 1.0中仅对元素的子元素进行分组?

时间:2015-12-16 07:54:32

标签: xml xslt

请注意,我只需要使用XSL 1.0版。 我的XML类似于:

<Person>
    <Tp>
        <Tl acc="999" />
        <Tl acc="999" />
        <Tl acc="777" />
    </Tp>
    <Tp>
        <Tl acc="999" />
        <Tl acc="888" />
    </Tp>
    <Tp>
        <Tl acc="999" />
        <Tl acc="888" />
        <Tl acc="888" />
    </Tp>
    <Tp>
        <Tl acc="666" />
    </Tp>
    <Tp>
        <Tl acc="555" />
        <Tl acc="333" />
        <Tl acc="555" />
        <Tl acc="555" />
    </Tp>
</Person>

预期输出为:

<Person>
    <Tp>
        <Acc>999</Acc>
        <Acc>777</Acc>
    </Tp>
    <Tp>
        <Acc>999</Acc>
        <Acc>888</Acc>
    </Tp>
    <Tp>
        <Acc>999</Acc>
        <Acc>888</Acc>
    </Tp>
    <Tp>
        <Acc>666</Acc>
    </Tp>
    <Tp>

        <Acc>555</Acc>
        <Acc>333</Acc>
    </Tp>
</Person>

我尝试过Muenchian分组。但问题是该技术识别整个文档中的唯一条目,即文档中的所有<Tl>标签。但是,我想仅识别特定<Tp>标记中的唯一条目。我已经尝试了以下内容:

并且每个如下

以上XSL返回以下内容:

<Person>
    <Tp>
        <Acc>999</Acc>
        <Acc>777</Acc>
    </Tp>
    <Tp>
        <Acc>888</Acc>
    </Tp>
    <Tp>
    </Tp>
    <Tp>
        <Acc>666</Acc>
    </Tp>
    <Tp>

        <Acc>555</Acc>
        <Acc>333</Acc>
    </Tp>
</Person>

来自第二和第三<Tp>的999和888是一个。

修改:添加了完整的XSL

    <?xml version="1.0" encoding="UTF-8"?>

    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:fn="http://www.w3.org/2005/xpath-functions"
        exclude-result-prefixes="xs fn">


        <xsl:key use="@acc" name="myaccounts" match="Tl"></xsl:key>


        <xsl:template match="/">
            <Person>
                <xsl:for-each select="Person/Tp">
                    <Tp>
                        <xsl:for-each
                            select="Tl[generate-id() = generate-id(key('myaccounts', @acc)[1])]">
                                <Tl>
                                <xsl:value-of select="@acc"></xsl:value-of>
                                </Tl>

                        </xsl:for-each>
                    </Tp>
                </xsl:for-each>
            </Person>
        </xsl:template>
    </xsl:stylesheet>

1 个答案:

答案 0 :(得分:2)

你并不遥远,但问题是你想要考虑Tp元素。由于Tp元素本身没有属性或子元素来唯一标识它们,因此您可以使用generate-id()函数,并在密钥中使用它

<xsl:key name="Tl" match="Tl" use="concat(generate-id(..), '|', @acc)" />

因此,您按Tp元素和@acc元素进行分组。注意管道|的使用是任意的。它只需要是@acc属性中没有出现的任何字符。

然后,假设您位于Tp元素上,则选择distint Tl元素,如此

<xsl:apply-templates select="Tl[generate-id() = generate-id(key('Tl', concat(generate-id(..), '|', @acc))[1])]" />

试试这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" />

    <xsl:key name="Tl" match="Tl" use="concat(generate-id(..), '|', @acc)" />

    <xsl:template match="Tp">
        <xsl:copy>
            <xsl:apply-templates select="Tl[generate-id() = generate-id(key('Tl', concat(generate-id(..), '|', @acc))[1])]" />
        </xsl:copy>
    </xsl:template>

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

请注意使用身份模板复制现有元素。