XSLT在多个条件上丢弃重复项

时间:2018-04-30 10:26:28

标签: xml xslt xslt-2.0

我有以下数据结构,需要使用v1v2的每个组合输出每个节点的ID,其中v等于A。< / p>

应打印带有2,3,4,6,7的节点。

<root>
    <node>
        <v>A</v>
        <id>2</id>
        <v1>S</v1>
        <v2>S</v2>
    </node>
    <node>
        <v>A</v>
        <id>3</id>
        <v1>S</v1>
        <v2>S1</v2>
    </node>
    <node>
        <v>A</v>
        <id>4</id>
        <v1>S2</v1>
        <v2>S1</v2>
    </node>
    <node>
        <v>B</v>
        <id>5</id>
        <v1>S2</v1>
        <v2>S3</v2>
    </node>
    <node>
        <v>A</v>
        <id>6</id>
        <v1>S2</v1>
        <v2>S3</v2>
    </node>
    <node>
        <v>A</v>
        <id>7</id>
        <v1>S</v1>
        <v2>S3</v2>
    </node>
    <node>
        <v>A</v>
        <id>8</id>
        <v1>S</v1>
        <v2>S</v2>
    </node>
</root>

我尝试使用xsl:key,但遗憾的是只打印了唯一的元素(缺少id = 2)

如下所示使用preceeding并未产生所需的结果。

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

    <!-- pos 1 -->
    <xsl:key name="keys" match="node" use="concat(v1, '|', v2, '|', v)"/>
    <!-- /pos 1 -->

    <xsl:template match="root">
        <xsl:for-each select="node[v='A']">

            <!-- pos 1 -->
            <xsl:variable name="vDups" select="key('keys', concat(v1, '|', v2, '|', v))[not(generate-id() = generate-id(current()))]" />
            <xsl:if test="not($vDups)">
                <node>
                    <xsl:value-of select="current()/id"/>
                </node>
            </xsl:if>
            <!-- /pos 1 -->

            <!-- pos 2 -->
            <xsl:if test="not(preceding::node/v1=current()/v1 and preceding::node/v2 = current()/v2)">
                <node>
                    <xsl:value-of select="id" />
                </node>
            </xsl:if>
            <!-- /pos 2 -->

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

如何达到预期效果?

3 个答案:

答案 0 :(得分:1)

更改密钥

<xsl:key name="keys" match="node" use="concat(v1, '|', v2, '|', v)"/>

<xsl:key name="keys" match="node" use="concat(v1, '|', v2, '|', v[.='A'])"/>

然后是身份模板

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

和无效模板,用于与第一次出现的键

不匹配的节点
<xsl:template match="node[not(generate-id()=generate-id(key('keys', concat(v1, '|', v2, '|', v))[1]))]"/>

在行动here中查看。

答案 1 :(得分:1)

试试这个:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="keys" match="node" use="concat(v1, '|', v2,'|',v)"/>

    <xsl:template match="node[v!='A']"/>

    <xsl:template match="node[generate-id()!=generate-id(key('keys', concat(v1,'|',v2,'|',v))[1])]"/>

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

这有一个模板可以排除A元素中没有v的任何内容,另一个模板排除任何不是给定组合中的第一个。我已经包含了身份模板以按原样输出剩余的节点,但您可以将其替换为您需要的node的任何处理。

免责声明:这是一个XSLT1解决方案,可能存在更高效的XSLT2解决方案。

答案 2 :(得分:1)

您已对此XSLT 2.0进行了标记,并在样式表中添加了version="2.0",在这种情况下,您可以使用xsl:for-each-group来简化您的XSLT

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

    <xsl:template match="root">
        <xsl:for-each-group select="node[v = 'A']" group-by="concat(v1, '|', v2)">
                <node>
                    <xsl:value-of select="id"/>
                </node>
        </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>