2012-06-20 68 views
6

我在Xpath 1.0中的字符串上遇到<運算符問題。如何比較字符串與Xpath 1.0?

這個簡單的XPath表達式

'A' < 'B' (or the equivalent 'A' &lt; 'B') 

沒有評估爲真,在我的libxslt XSLT運行(這是一個XSLT 1.0引擎)。

我檢查了XML Spy,它允許在1.0和2.0中測試Xpath表達式,當然,在Xpath 2.0中它的計算結果爲true,但在Xpath 1.0中它的計算結果爲false

這是Xpath 1.0中的錯誤嗎?

我應該用什麼其他表達式來比較兩個字符串/字符的字母順序?請注意,compare()函數不會執行,因爲這是一個XSLT 2.0函數。

回答

4

是的,這是XPath 1.0的限制。 (我認爲將你不喜歡的限制稱爲「bug」是不合理的,儘管XPath 2.0的設計者明確同意這是一個不受歡迎的限制)。

您已經標記問題「XSLT」,所以你可能能夠解決此問題在XSLT水平,至少如果您的處理器具有節點集擴展:

<xsl:variable name="nodes"> 
    <node><xsl:value-of select="$A"/></node> 
    <node><xsl:value-of select="$B"/></node> 
</xsl:variable> 

<xsl:for-each select="exslt:node-set($nodes)/*"> 
    <xsl:sort select="."/> 
    <xsl:if test="position()=1 and .=$A">A comes first!</xsl:if> 
</xsl:for-each> 

但也許現在是時候轉移到2.0。什麼阻止你回來?

+0

謝謝邁克爾 - 不錯的緊湊解決方案。至於XSLT 2.0,有什麼讓我反感 - 'libxslt'是 - 這是'php 5'使用的引擎,我無法改變它。也許在將來,我的託管服務商將使用一個使用XSLT 2.0引擎的PHP版本 - 如果有的話。當然,我真的很想在XSLT 2.0中做所有這些事情 - 實際上我是爲了開發而做的,然後不得不重寫所有內容。我認爲,對於大量的XSLT開發人員來說,不移動到XSLT 2.0的原因是相同的。 – Maestro13

+0

@ Maestro13:Zobra支持XPath 2.0並可作爲PHP擴展使用,請參閱:http://www.ibm.com/developerworks/xml/library/x-zorba/index.html - 就PHP而言,您可以[註冊PHP函數](http://php.net/manual/en/domxpath.registerphpfunctions.php)像'strcmp'用於你的xpath。 – hakre

+0

@hakre謝謝你的信息 - 我會去檢查我的託管服務提供商是否可以激活Zobra。是的,另一種方法是註冊一個自定義php函數,並在xslt中使用它 - 在XSLT戰鬥中,我完全忘記了這一點。 – Maestro13

1

這可能是難看的解決方案,在許多情況下不可行,但對於簡單的字母順序比較,您可以使用translate。下面的代碼片斷只是一個可以furtherly擴展的例子:

translate('A','ABCD','1234') &lt; translate('B','ABCD','1234'); 

您的翻譯表達應涵蓋所有的信件,並低的情況下,並且可以通過定義命名模板可以方便地重複使用。

+0

所以這是一個錯誤,這是一個解決方法?啊! – Maestro13

+0

只是一個限制。看看http://www.xsltfunctions.com/xsl/c0008.html#c0017 –

+0

我認爲這種方法把「D」放在「AA」之前(4 <11) –

7

在XPath 1.0中,只對=!=定義了字符串比較,並且不提供排序比較。該規範表示

當待比較既不對象是一個節點集合和操作員是 < =,<,> =或>,則目的是通過既 對象轉換爲數字和比較所述比較根據IEEE 754.

因此,您的操作數都被轉換爲浮點數,使它們都是NaN。

我相信微軟的XML增加了擴展功能來處理這個問題,當然這隻有在你使用MSXML的時候纔有用。

2

爲了證明這對其他人也有用,下面是我根據邁克爾凱的建議寫的代碼。我寫了一個自定義的compare函數,它提供了與Xpath 2.0相同的結果。我還爲該問題添加了php標籤,以便更頻繁地找到它。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:func="http://exslt.org/functions" 
    xmlns:common="http://exslt.org/common" 
    xmlns:custom="urn:myCustomFunctions" 
    exclude-result-prefixes="func common custom" 
    extension-element-prefixes="func custom"> 

    <xsl:output method="xml"/> 

    <func:function name="custom:compare"> 
     <xsl:param name="string1"/> 
     <xsl:param name="string2"/> 

     <func:result> 
      <xsl:choose> 
       <xsl:when test="$string1 = $string2">0</xsl:when> 
       <xsl:otherwise> 
        <xsl:variable name="nodes"> 
         <node><xsl:value-of select="$string1"/></node> 
         <node><xsl:value-of select="$string2"/></node> 
        </xsl:variable> 
        <xsl:for-each select="common:node-set($nodes)/*"> 
         <xsl:sort select="."/> 
         <xsl:choose> 
          <xsl:when test="position()=1 and .=$string1">-1</xsl:when> 
          <xsl:when test="position()=1 and .=$string2">1</xsl:when> 
         </xsl:choose> 
        </xsl:for-each> 
       </xsl:otherwise> 
      </xsl:choose> 
     </func:result> 
    </func:function> 

    <xsl:template match="/"> 
     <out> 
      <test1><xsl:value-of select="custom:compare('A', 'B')"/></test1> 
      <test2><xsl:value-of select="custom:compare('A', 'A')"/></test2> 
      <test3><xsl:value-of select="custom:compare('C', 'B')"/></test3> 
      <test4><xsl:value-of select="custom:compare('DD', 'A')"/></test4> 
     </out> 
    </xsl:template> 

</xsl:stylesheet> 

運行這個(與虛擬輸入)的結果是

<?xml version="1.0"?> 
<out> 
    <test1>-1</test1> 
    <test2>0</test2> 
    <test3>1</test3> 
    <test4>1</test4> 
</out> 

對於那些誰願意來測試這個在PHP自己,這是我使用的代碼:

<?php 
$xslt = new XSLTProcessor(); 
$xslt->importStylesheet(DOMDocument::load('testCompare.xslt')); 
$xslt -> registerPHPFunctions(); 
$xml = new SimpleXMLElement('<test/>'); 
print $xslt->transformToXML($xml); 
?>