XSLT将数字比较为字符串

Joh*_*van 7 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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

预期产出:

<?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>
Run Code Online (Sandbox Code Playgroud)

实际产量:

<?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>
Run Code Online (Sandbox Code Playgroud)

Mad*_*sen 5

看来在XSLT/XPATH 1.0中,string()在执行比较时仍将值评估为数字.

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

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

使用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>
Run Code Online (Sandbox Code Playgroud)

但是将值与字符串进行比较就足够了'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>
Run Code Online (Sandbox Code Playgroud)

  • 我同意分析,但对于XSLT 2.0,只需使用`<xsl:if test =".&lt;'7'">`和`version ="2.0"`样式表,建议`<xsl:if test = "xs:string(.)&lt; xs:string('7')">`我担心人们认为他们必须在`xs:string`构造函数调用中包装任何内容. (2认同)