按孙子项的值对节点进行分组

时间:2019-02-13 15:30:46

标签: xml xslt-1.0

我有一个XML,该XML具有一个组类型作为最深的节点。我必须对XML的一个子部分进行分组,否则将返回完全相同的XML。

我在Stackoverflow中研究了不同的问题。具体来说,我一直基于这一答案: Grouping xml nodes by value of a child in Xsl

输入XML如下:

<list>
    <element>
        <foo>
            <bar>
            </bar>
        </foo>
        <values>
            <position>
                <foo2>
                </foo2>
                <type>first
                </type>
            </position>
            <position>
                <foo2>
                </foo2>
                <type>second
                </type>
            </position>
            <position>
                <foo2>
                </foo2>
                <type>first
                </type>
            </position>
        </values>
    </element>    
</list>

输出XML应该如下所示:

<list>
    <element>
        <foo>
            <bar>
            </bar>
        </foo>
        <types>
            <first>
                <values>
                    <position>
                        <foo2>
                        </foo2>
                    </position>
                    <position>
                        <foo2>
                        </foo2>
                    </position>
                </values>
            </first>
            <second>
                <values>
                    <position>
                        <foo2>
                        </foo2>
                    </position>
                </values>
            </second>
        </types>
    </element>    
</list>

到目前为止,我有以下XLST,它会将转换复制到顶部:          

    <xsl:key name="types" match="type" use="."/>

    <xsl:template match="/list">
        <root>
            <xsl:apply-templates select="element/values/position/type[generate-id() = generate-id(key('types', .)[1])]"/>
            <xsl:apply-templates select="*|@*|comment()|text()"/>
        </root>
    </xsl:template>

    <xsl:template match="type">
        <xsl:variable name="currentType" select="."/>
        <xsl:element name="{$currentType}">
            <xsl:apply-templates select="*|@*|comment()|text()"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="*">
        <xsl:element name="{local-name(.)}">
            <xsl:apply-templates select="*|@*|comment()|text()"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

一旦我将一个应用模板放在父节点上(在与类型匹配的模板中),它就不再返回任何内容:

    <xsl:template match="type">
        <xsl:variable name="currentType" select="."/>
        <xsl:element name="{$currentType}">
            <xsl:apply-templates select="../../*|@*|comment()|text()"/>
        </xsl:element>

1 个答案:

答案 0 :(得分:1)

第一件事:您想将position节点按公用type分组-因此,您应该使密钥匹配position并使用type

一旦您这样做,就可以很简单:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="pos-by-type" match="position" use="type"/>

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

<xsl:template match="values">
    <types>
        <xsl:for-each select="position[generate-id() = generate-id(key('pos-by-type', type)[1])]">
            <xsl:element name="{normalize-space(type)}">
                <values>
                    <xsl:apply-templates select="key('pos-by-type', type)"/>
                </values>
            </xsl:element>
        </xsl:for-each>
    </types>
</xsl:template>

<xsl:template match="type"/>

</xsl:stylesheet>

请注意,这取决于type包含的字符串,该字符串是有效的XML元素名称。在您的示例中不是,我使用了normalize-space()。但是您的实际输入可能包含其他字符串,还有其他问题。