分层XSL如何排序?

How do hierarchical XSL sorting?

我有以下输入 XML:

<?xml version="1.0" encoding="UTF-8"?>
<soldiers>
    <soldier>
        <name>John</name>
        <supervisor>Marcus</supervisor>
    </soldier>
    <soldier>
        <name>Marcus</name>
        <supervisor>Mike</supervisor>
    </soldier>
    <soldier>
        <name>Frank</name>
        <supervisor>Marcus</supervisor>
    </soldier>
    <soldier>
        <name>Mike</name>
        <supervisor>Anna</supervisor>
    </soldier>
</soldiers>

现在我正在寻找一种根据主管标签对 XML 层次结构进行排序的方法。执行此操作的最高效方法是什么?给定示例的结果应如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<soldiers>
    <soldier>
        <name>Mike</name>
        <supervisor>Anna</supervisor>
    </soldier>
    <soldier>
        <name>Marcus</name>
        <supervisor>Mike</supervisor>
    </soldier>
    <soldier>
        <name>John</name>
        <supervisor>Marcus</supervisor>
    </soldier>
    <soldier>
        <name>Frank</name>
        <supervisor>Marcus</supervisor>
    </soldier>
</soldiers>

这里没有列出迈克的主管,因此他排在最前面。 Marcus 的主管是 Mike,因此他隶属于 Mike。 John和Frank的上司是Marcus,所以排在最后。

您可以使用 key 跟随参考文献:

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

    <xsl:output indent="yes"/>

    <xsl:key name="ref" match="soldier" use="supervisor"/>

    <xsl:template match="soldiers">
        <xsl:copy>
            <xsl:apply-templates select="soldier[not(supervisor = ../soldier/name)]"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="soldier">
        <xsl:copy-of select="."/>
        <xsl:apply-templates select="key('ref', name)"/>
    </xsl:template>
</xsl:transform>

根据@michael.hor257k 的评论,您可能更想要

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:mf="http://example.com/mf" exclude-result-prefixes="mf">

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

    <xsl:key name="ref" match="soldier" use="supervisor"/>

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

    <xsl:function name="mf:refs" as="element(soldier)*">
        <xsl:param name="input" as="element(soldier)*"/>
        <xsl:copy-of select="$input"/>
        <xsl:sequence select="if (key('ref', $input/name, $main-root)) then mf:refs(key('ref', $input/name, $main-root)) else ()"/>
    </xsl:function>

    <xsl:template match="soldiers">
        <xsl:copy>
            <xsl:sequence select="mf:refs(soldier[not(supervisor = ../soldier/name)])"/>
        </xsl:copy>
    </xsl:template>

</xsl:transform>

它使用相同的密钥,但在递归到下一个级别之前首先完全输出每个级别。