XSL:通过比较节点来比较节点

时间:2013-01-19 05:28:10

标签: xslt xslt-1.0

我希望能够根据子节点的值来比较两个节点。使用=运算符测试节点相等性只是比较各个节点的字符串值。我想根据子节点中的值来比较它们。

更具体一点,我希望<a><b>(下方)相等,因为@id元素的<c>值相同具有匹配@type属性的属性也具有匹配的@id属性。

<a>
    <c type="type-one" id="5675"/>
    <c type="type-two" id="3423"/>
    <c type="type-three" id="9088"/>
</a>
<b>
    <c type="type-one" id="5675"/>
    <c type="type-two" id="3423"/>
</b>

但这些会有所不同:

<a>
    <c type="type-one" id="5675"/>
</a>
<b>
    <c type="type-one" id="2342"/>
</b>

我可以开始看到的唯一解决方案是与for-each语句进行费力的比较,如果可能,我希望避免这种说法。

如果可能,我想坚持使用XSLT 1.0。我正在使用xsltproc。

2 个答案:

答案 0 :(得分:0)

首先,名为“equals”的关系不能具有该名称

“等于”意味着关系是等价关系。根据定义,任何等价关系~必须是:

  1. 自反:x ~ x

  2. 对称:如果x ~ yy ~ x

  3. 传递:如果x ~ yy ~ zx ~ z

  4. 以下是一个示例,显示建议的“等于”关系不具有传递性:

    x是:

    <a>
        <c type="type-one" id="5675"/>
        <c type="type-two" id="3423"/>
        <c type="type-three" id="9088"/>
    </a>
    

    y是:

    <b>
        <c type="type-one" id="5675"/>
        <c type="type-two" id="3423"/>
        <c type="type-four" id="1234"/>
    </b>
    

    z是:

    <b>
        <c type="type-three" id="3333"/>
        <c type="type-four" id="1234"/>
    </b>
    

    现在,我们可以看到x ~ yy ~ z。但是,显然这不成立:x ~ z

    这就是说,我称之为“匹配”的关系,它是放松的而不是“等于”。

    这是问题的解决方案,具有上述调整

    请注意,这不能用单个XPath表达式表示,因为XPath 1.0(在XSLT 1.0转换中使用)没有范围变量

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="/*">
         <xsl:call-template name="matches">
           <xsl:with-param name="pElem1" select="a"/>
           <xsl:with-param name="pElem2" select="b"/>
         </xsl:call-template>
     </xsl:template>
    
     <xsl:template name="matches">
       <xsl:param name="pElem1" select="/.."/>
       <xsl:param name="pElem2" select="/.."/>
    
       <xsl:variable name="vMisMatch">
           <xsl:for-each select="$pElem1/c[@type = $pElem2/c/@type]">
            <xsl:if test=
               "$pElem2/c[@type = current()/@type and not(@id = current()/@id)]">1</xsl:if>
           </xsl:for-each>
       </xsl:variable>
    
       <xsl:copy-of select="not(string($vMisMatch))"/>
     </xsl:template>
    </xsl:stylesheet>
    

    对以下XML文档应用此转换时:

    <t>
        <a>
            <c type="type-one" id="5675"/>
            <c type="type-two" id="3423"/>
            <c type="type-three" id="9088"/>
        </a>
        <b>
            <c type="type-one" id="5675"/>
            <c type="type-two" id="3423"/>
        </b>
    </t>
    

    产生了想要的正确结果:

    true
    

    对此XML文档应用相同的转换时:

    <t>
        <a>
            <c type="type-one" id="5675"/>
            <c type="type-two" id="3423"/>
            <c type="type-three" id="9088"/>
        </a>
        <b>
            <c type="type-one" id="5675"/>
            <c type="type-two" id="9876"/>
        </b>
    </t>
    

    再次产生正确的结果:

    false
    

答案 1 :(得分:0)

这是我想出的。给出这样的玩具数据集:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <a>
        <item key="x" value="123"/>
        <item key="y" value="456"/>
        <item key="z" value="789"/>
    </a>
    <b>
        <item key="x" value="123"/>
        <item key="z" value="789"/>
    </b>
</root>

此样式表显示了如何测试相等性,如问题中所定义。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:set="http://exslt.org/sets"  xmlns:exsl="http://exslt.org/common" 
 extension-element-prefixes="set exsl">
<xsl:output method="text" version="1.0" encoding="UTF-8"/> 

<xsl:template match="/">
    <xsl:variable name="values-are-equal">
        <xsl:call-template name="equal">
            <xsl:with-param name="A" select="/root/a"/>
            <xsl:with-param name="B" select="/root/b"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:choose>
        <xsl:when test="$values-are-equal = 1">Equal</xsl:when>
        <xsl:otherwise>Inequal</xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="equal">
    <xsl:param name="A" />
    <xsl:param name="B" />
    <xsl:variable name="common-keys" select="$A/item/@key[ count(set:distinct(  . | $B/item/@key )) = count( set:distinct( $B/item/@key ) ) ]"/>
    <xsl:variable name="differences">
        <xsl:for-each select="$common-keys">
            <xsl:if test="$A/item[@key = current()]/@value != $B/item[@key = current()]/@value">
                <different/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <xsl:choose>
        <xsl:when test="count( exsl:node-set($differences)/* ) > 0">0</xsl:when>
        <xsl:otherwise>1</xsl:otherwise>
    </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

这使用了一些扩展,这些扩展在xsltproc和其他处理器中可用。