2013-01-19 40 views
1

我想能夠根據其子節點的值比較兩個節點。使用=運算符測試節點的相等性只是比較各個節點的字符串值。我想根據它們的子節點中的值來比較它們。XSL:通過比較節點比較他們的子節點

爲了更具體一點,我想<a><b>(如下)是相等的,因爲@id的值對於具有匹配@type屬性也具有匹配@id屬性<c>元件是相同的。

<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。

+1

第一個例子中'a'和'b'如果'a'有一個子元素''有一個相應的孩子'B'?請編輯問題並更正。目前的例子與「平等」的概念相矛盾。 –

+0

沒有平等的單一定義...例如,具有相同的內部文本節點在某種意義上是平等的,但是這不是我感興趣的,或者在現實生活中的差異,也許是兩個人有相同的口碑堆棧溢出,但不平等的工資,等等,等等等等... –

+0

user1447002,平等被嚴格定義爲一種等價關係 - 如果是〜平等的,它必須是1自反,對稱2和3傳遞。你沒有這些 - 你沒有定義你稱之爲「平等」的關係的所有屬性。 –

回答

0

首先,名爲「equals」的關係不能有這個名字

「等於」意味着該關係是等價關係。根據定義任何等價關係~必須是:

  1. 自反:x ~ x

  2. 對稱:如果x ~ y然後y ~ x

  3. 傳遞:如果x ~ yy ~ z然後x ~ 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 
+0

不錯的答案!你還可以檢查[我的問題](http://stackoverflow.com/questions/39344435/xslt-transform-nodes-by-comparing-their-child-nodes)?我真的很感激能夠找到更多有關如何實現這一目標的信息! – 2016-09-06 09:07:37

+0

@ Dex'ter,我看到你的問題已經回答了(當我睡覺的時候)。你需要額外的幫助嗎? –

+0

如果你有什麼要添加到該答案任何額外的信息將是偉大的:)謝謝 – 2016-09-06 15:52:39

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

+0

沒有必要使用任何擴展和更簡單/更短的解決方案存在 - 請參閱我的答案。 –