XSLT отбрасывает дубликаты по нескольким критериям - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть следующая структура данных, и мне нужно вывести идентификаторы каждого узла с каждой комбинацией v1 и v2 ровно один раз, где v равно A.

Узлы с идентификаторами 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 голосов
/ 30 апреля 2018

Попробуйте это:

<?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.

0 голосов
/ 30 апреля 2018

Вы пометили этот 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>
0 голосов
/ 30 апреля 2018

смени ключ

<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]))]"/>

увидеть это в действии здесь .

...