XSLT 2.0 将属性值序列与变量进行比较

XSLT 2.0 Compare sequence of attribute values to variable

我有

等元素
<elem attr1="value1 someValue" attr2="value2 someOtherValue"/>

具有可变数量属性的元素,每个元素具有可变数量的值。

其中一些属性的名称和它们的一些值预先保存在变量中,并根据这些变量检查元素。

我需要检查元素是否具有在该变量(我开始工作)中指定的属性,以及对于每个属性,它的至少一个值是否是另一个变量中指定的值之一。

所以如果变量包含“Value1 Value2”(或更多值)

元素

<elem attr1="Value1 SomeValue" attr2="Value1 someOtherValue AthirdValue" attr3="value2 otherValue"/>

符合要求(前提是attr1,attr2,attr3是正确的属性名,我之前查过),但是element:

<elem attr1="Value1 SomeValue" attr2="anything someOtherValue" attr3="value otherValue"/>

不符合要求,因为其中一个属性没有包含在变量中的值(attr2:变量中既没有指定anything也没有指定someOtherValue ).

我尝试标记这些值,但即使只有一个属性具有正确值之一,元素也被认为是正确的。我需要确保所有属性至少有一个正确的值。

非常感谢帮助和提示!

编辑:

变量取值方式:

<xsl:variable name="filterName" select="$someDoc//someElem[@id = $curID]//filters/@value"/>

其他文件:

<?xml version="1.0"/>
<doc>
<filters name="attr1" value="value1"/>
<filters name="attr2" value="value2"/>
<filters name="attr3" value="value2"/>
</doc>

**EDIT 2:**

查看上面的过滤器元素,元素可能如下所示:

<elem/> <!-- no attribute -->
<elem someattr="somevalue"/> <!-- some irrelevant attribute -->
<elem attr1="value1"/> <!-- one of the attributes -->
<elem attr1="value1" attr2="value"/> <!-- two of the attributes -->

因此 filters 元素的 @name 属性指定了 elem 元素上的属性名称,过滤器元素的 @value 属性指定了值该属性必须具有才能满足要求。

解决方案(改编自答案post)

<xsl:when test="count(@*[local-name() = $filterNames]) > 1">
<!-- element has more then one filter attribute -->
    <xsl:variable name="currentFilterValues">
        <xsl:value-of select="$filterValues"/>
    </xsl:variable>
    <xsl:variable name="attributeNamesOfCurrentElement">
        <xsl:value-of select="@*[local-name() = $filterNames]/fn:local-name()"/>
    </xsl:variable>
    <xsl:variable name="errors">
        <xsl:for-each select="tokenize($attributeNamesOfCurrentElement, '\s+')">
            <xsl:variable name="currentName" select="."/>
            <xsl:variable name="currentValue" select="$currentElement/@*[fn:local-name() = $currentName]"/>
            <xsl:if test="not(tokenize($currentValue, '\s+') = tokenize($currentFilterValues, ' '))">
                <error/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <xsl:choose>
        <xsl:when test="$errors/error">
            <!-- at least one attribute doesnt have any of the required values -->
        </xsl:when>
        <xsl:otherwise>
            <!-- all attributes have at least one of the required values -->
            <xsl:copy>
                <xsl:apply-templates select="$currentElement_2/@*"/>
                <xsl:if test="child::*">
                    <xsl:for-each select="child::*">
                        <xsl:call-template name="recurse">
                            <xsl:with-param name="filterNames" select="$filterNames"/>
                            <xsl:with-param name="filterValues" select="$filterValues"/>
                            <xsl:with-param name="filterType" select="$filterType"/>
                            <xsl:with-param name="currentElement_2" select="."/>
                        </xsl:call-template>
                    </xsl:for-each>
                </xsl:if>
            </xsl:copy>
        </xsl:otherwise>
    </xsl:choose>
</xsl:when>

这有点抽象,但为了说明一个原理,让我们有以下内容:

XML

<root>
    <elem attr1="alpha charlie" attr2="delta alpha echo" attr3="foxtrot bravo golf"/>
    <elem attr1="alpha charlie" attr2="hotel delta" attr3="bravo india"/> 
</root>

XSLT 2.0

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

<xsl:variable name="values" select="('alpha', 'bravo')"/>

<xsl:template match="/root">
    <xsl:copy>
        <xsl:apply-templates select="elem"/>
    </xsl:copy>
</xsl:template>     

<xsl:template match="elem">
    <xsl:copy>
        <xsl:choose>
            <xsl:when test="@*[not(tokenize(., ' ')=$values)]">
                <xsl:text>NO</xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <xsl:text>YES</xsl:text>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

结果

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <elem>YES</elem>
   <elem>NO</elem>
</root>

说明

测试:

test="@*[not(tokenize(., ' ')=$values)]"

returns 当至少有一个属性在标记化后不包含与 $values 变量中的值之一匹配的任何标记时为真。


编辑

针对您修改后的要求,我认为这应该可行:

XSLT 2.0

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

<xsl:variable name="filter-doc" select="document('filter.xml')"/>

<xsl:key name="filter-by-name" match="filters" use="@name" />

<xsl:template match="/root">
    <xsl:copy>
        <xsl:apply-templates select="elem"/>
    </xsl:copy>
</xsl:template>     

<xsl:template match="elem">
    <xsl:variable name="strikes">
        <xsl:for-each select="@*[key('filter-by-name', name(), $filter-doc)]">
            <xsl:variable name="values" select="key('filter-by-name', name(), $filter-doc)/@value" />
            <xsl:if test="not(tokenize(., ' ')=$values)">
                <strike/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>

    <xsl:copy>
        <xsl:choose>
            <xsl:when test="$strikes/strike">
                <xsl:text>NO</xsl:text>
            </xsl:when>
            <xsl:otherwise>
                <xsl:text>YES</xsl:text>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

这可能会稍微简化一些,但首先我想知道它是否提供了正确的答案。