7

背景

我最近惊讶地发现 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>
4

3 回答 3

5

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

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

当要比较的对象都不是节点集并且运算符是 <=、<、>= 或 > 时,通过将两个对象都转换为数字并根据 IEEE 754 比较数字来比较对象。< 比较将是当且仅当第一个数字小于第二个数字时才为真。

使用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>
于 2016-01-12T15:39:31.077 回答
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>
于 2016-01-12T15:21:27.313 回答
1

请注意,在 XSLT 1.0 中,两者都'a' > 'b'评估'b' > 'a'false.

于 2016-01-12T15:58:32.103 回答