如何根据属性值合并节点?

时间:2015-09-22 20:52:30

标签: xslt xslt-1.0 muenchian-grouping xslkey

在第一次xsl转换后,我有一个类似于下面的xml输出:

<?xml version="1.0" encoding="UTF-8"?>
<analysis type="1">
    <file path="a.txt">
        <line nb="23" found="true"/>
        <line nb="36" found="true" count="2"/>
        <line nb="98" found="true"/>
    </file>
    <file path="a.txt">
        <line nb="100" found="false"/>
    </file>
    <file path="b.txt">
        <line nb="10" found="false"/>
    </file>
    <!-- more file nodes below with different @path -->
</analysis>

但是现在我需要获得第二个输出,如果它们具有相同的file属性,则合并path个节点,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<analysis type="1">
    <file path="a.txt">
        <line nb="23" found="true"/>
        <line nb="36" found="true" count="2"/>
        <line nb="98" found="true"/>
        <line nb="100" found="false"/>
    </file>
    <file path="b.txt">
        <line nb="10" found="false"/>
    </file>
</analysis>

我事先并不知道可能的@path值。

我查看了有关节点合并的多个帖子,但找不到按我想要的方式。我失去了节点分组,密钥,id生成......到目前为止只获得了错误消息。

请你帮我从第一个输出开始(xls 1.0)?如果你能提供一些参考资料(网站),我可以找到有关这种变换的解释,那将非常棒。

注意:具有相同@nb的两个line个节点的两个file节点的@path属性永远不会发生冲突,它是唯一的,即不会发生这种情况:< / p>

<?xml version="1.0" encoding="UTF-8"?>
<analysis type="1">
    <file path="a.txt">
        <line nb="36" found="true" count="2"/>
    </file>
    <file path="a.txt">
        <line nb="36" found="true"/>
    </file>
</analysis>

非常感谢你的帮助!

1 个答案:

答案 0 :(得分:2)

没有密钥的XPath 1.0

由于您在问题中说明您无法理解密钥,因此使用一种称为兄弟递归的技术,这是使用无密钥的一种方法。它被认为不如使用键好,因为它使用兄弟轴,这通常很慢。但是,在大多数实际情况中,您不会注意到差异:

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

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

    <xsl:template match="analysis">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="file[not(preceding-sibling::file/@path = @path)]" mode="sibling-recurse" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="file" mode="sibling-recurse">
        <xsl:copy>
            <!-- back to default mode -->
            <xsl:apply-templates select="node() | @*" />
            <xsl:apply-templates select="following-sibling::file[current()/@path = @path]" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="file">
        <xsl:apply-templates select="node()" />
    </xsl:template>
</xsl:stylesheet>

带有Münchian分组键的XPath 1.0

这种方法使用Münchian分组,这在其他地方有解释(只需按照教程like this one一起使用此代码)。它也使用同级轴,但破坏性较小(即,不需要在每个单节点测试中遍历整个同级轴)。

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

    <xsl:key match="file" use="@path" name="path" />

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

    <xsl:template match="analysis">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="file[generate-id(.) = generate-id(key('path', @path))]" mode="sibling-recurse" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="file" mode="sibling-recurse">
        <xsl:copy>
            <!-- back to default mode -->
            <xsl:apply-templates select="node() | @*" />
            <xsl:apply-templates select="following-sibling::file[@path = current()/@path]/node()" />
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

注意:对于这两种方法,模式切换并不是完全必要的,但它可以更容易地编写简单的匹配模式并防止优先级冲突或难以发现的错误(imo)。