XSLT将数字比较为字符串

时间:2016-01-12 14:56:59

标签: xslt comparison string-comparison numeric-conversion

背景

我最近惊讶地注意到XSL能够智能地处理数字;即在执行比较时知道将文本中的数字视为数字(即,它理解为7 < 10而不是思考'10' < '7')。就我而言,这就是我想要的;只是不是我所期待的。

出于好奇,我试图强迫XSLT将数字作为字符串进行比较(即使用string()函数,但没有运气。

问题

是否可以让XSLT将数字作为字符串进行比较;例如所以'10' < '7'

示例

源XML:

<?xml version="1.0" encoding="utf-8"?>
<element>
  <x>1</x>
  <x>2</x>
  <x>3</x>
  <x>4</x>
  <x>5</x>
  <x>6</x>
  <x>7</x>
  <x>8</x>
  <x>9</x>
  <x>10</x>
</element>

XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="yes"/>
  <xsl:template match="element">
    <element>

      <AsItComes>
        <xsl:for-each select="./x">
          <xsl:if test="./text() &lt; 7">
            <xsl:copy-of select="."></xsl:copy-of>
          </xsl:if>
        </xsl:for-each>
      </AsItComes>

      <AsNumber>
      <xsl:for-each select="./x">
        <xsl:if test="number(./text()) &lt; 7">
          <xsl:copy-of select="."></xsl:copy-of>
        </xsl:if>
      </xsl:for-each>
      </AsNumber>

      <AsString>
        <xsl:for-each select="./x">
          <xsl:if test="string(./text()) &lt; '7'">
            <xsl:copy-of select="."></xsl:copy-of>
          </xsl:if>
        </xsl:for-each>
      </AsString>

    </element>
  </xsl:template>
</xsl:stylesheet>

预期产出:

<?xml version="1.0" encoding="utf-8"?>
<element>
  <AsItComes>
    <x>1</x>
    <x>2</x>
    <x>3</x>
    <x>4</x>
    <x>5</x>
    <x>6</x>
    <x>10</x>
  </AsItComes>
  <AsNumber>
    <x>1</x>
    <x>2</x>
    <x>3</x>
    <x>4</x>
    <x>5</x>
    <x>6</x>
  </AsNumber>
  <AsString>
    <x>1</x>
    <x>2</x>
    <x>3</x>
    <x>4</x>
    <x>5</x>
    <x>6</x>
    <x>10</x>
  </AsString>
</element>

实际输出:

<?xml version="1.0" encoding="utf-8"?>
<element>
  <AsItComes>
    <x>1</x>
    <x>2</x>
    <x>3</x>
    <x>4</x>
    <x>5</x>
    <x>6</x>
  </AsItComes>
  <AsNumber>
    <x>1</x>
    <x>2</x>
    <x>3</x>
    <x>4</x>
    <x>5</x>
    <x>6</x>
  </AsNumber>
  <AsString>
    <x>1</x>
    <x>2</x>
    <x>3</x>
    <x>4</x>
    <x>5</x>
    <x>6</x>
  </AsString>
</element>

3 个答案:

答案 0 :(得分:5)

似乎在 XSLT / XPATH 1.0 中,string()值在执行比较时仍会被评估为数字。

https://www.w3.org/TR/xpath/#booleans

  

当要比较的对象都不是节点集而运算符是   &lt; =,&lt;,&gt; =或&gt;,然后通过转换两者来比较对象   对象到数字并根据IEEE 754比较数字。   &lt;当且仅当第一个数字较小时,比较才会成立   而不是第二个数字。

使用 XSLT / XPATH 2.0 (以及3.0和3.1),您可以将数据类型显式设置为xs:string,以确保对字符串执行比较而不是强制转换为数字值。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                version="2.0">
 <xsl:template match="element">
    <element>
      <AsString>
        <xsl:for-each select="./x">
          <xsl:if test="xs:string(.) &lt; xs:string('7')">
            <xsl:copy-of select="."></xsl:copy-of>
          </xsl:if>
        </xsl:for-each>
      </AsString>
    </element>
 </xsl:template>
</xsl:stylesheet>

但是将值与字符串'7'进行比较就足够了(同样,您可以删除<xsl:if>并将过滤器放在谓词中):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
            version="2.0">
 <xsl:template match="element">
    <element>
      <AsString>
        <xsl:for-each select="./x[. &lt; '7']">
          <xsl:copy-of select="."></xsl:copy-of>
        </xsl:for-each>
      </AsString>
    </element>
 </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:1)

如果您要使用第一个数字,那么解决方法可能就是将第一个位置子串起来。

<xsl:if test="substring(./text(), 1, 1) &lt; '7'">

返回

<AsString>
  <x>1</x>
  <x>2</x>
  <x>3</x>
  <x>4</x>
  <x>5</x>
  <x>6</x>
  <x>10</x>
</AsString>

答案 2 :(得分:1)

请注意,在XSLT 1.0中,'a' > 'b''b' > 'a'的评估均为false