XSL:通过比较它们的子节点比较节点(XSL: Comparing nodes by compari

2019-10-17 14:21发布

我希望能够根据自己的子节点的值来比较两个节点。 与测试节点平等=操作者只是各个节点的字符串值进行比较。 我想基于其子节点的值进行比较。

要有点更具体而言,我想<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的。

Answer 1:

首先,所谓的关系“等于”不能有这个名字

“等于”是指该关系是等价关系。 根据定义任何等价关系~必须是:

  1. 自反: x ~ x

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

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

下面是一个例子,表明所提出的“等于”关系是不可传递的:

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


Answer 2:

以下是我想出了。 由于玩具的数据集是这样的:

<?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和其它处理器可用。



文章来源: XSL: Comparing nodes by comparing their child nodes
标签: xslt xslt-1.0