【问题标题】:XPath 1.0, Interpret hex attribute as a numberXPath 1.0,将十六进制属性解释为数字
【发布时间】:2015-02-05 16:48:58
【问题描述】:

我需要比较表示整数的 XML 属性,但可以使用 XPath/XSLT-1.0 以十进制或十六进制(带有 0x 前缀)给出。

这是一个(不工作的)XSLT 来演示:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="iso-8859-1" omit-xml-declaration="yes" />

    <xsl:template match="//node">
        <xsl:if test="@value &gt; 2">
            <xsl:value-of select="@value"/>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

这是要输入的 XML:

<?xml version="1.0" encoding="UTF-8"?>
<body>
    <node value="1"/>
    <node value="3"/>
    <node value="0x03"/>
</body>

这是所需的输出。格式不重要;重要的只是在第二个和第三个节点上有一个匹配:

3 0x03

第二个节点上只有一个匹配; XML 不会将十六进制节点解释为数字。谁能想到这个问题的合理解决方案?

【问题讨论】:

  • 您使用的 XSLT 处理器是什么?这不能在 XSLT 1.0 中一步完成。您必须使用 converted numbersthat 创建一个中间结果(结果树片段) - 在 node-set() 扩展功能的帮助下 - 您可以按照您想要的方式选择节点( //node[@value &amp;gt; 2])。由于 node-set() 并非随处可用,因此取决于您的 XSLT 处理器版本。
  • @Tomalak 为什么你认为有必要有一个节点集?

标签: xslt xpath xslt-1.0


【解决方案1】:

格式不重要;

那么为方便起见,我将使用 XML 格式作为输出进行演示:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/body">
    <xsl:copy>
        <xsl:for-each select="node">
            <xsl:variable name="decimal">
                <xsl:choose>
                    <xsl:when test="starts-with(@value, '0x')">
                        <xsl:call-template name="hex2num">
                            <xsl:with-param name="hex" select="substring-after(@value, '0x')"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="@value"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <xsl:if test="$decimal > 2">
                <xsl:copy-of select="."/>
            </xsl:if>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

<xsl:template name="hex2num">
    <xsl:param name="hex"/>
    <xsl:param name="num" select="0"/>
    <xsl:param name="MSB" select="translate(substring($hex, 1, 1), 'abcdef', 'ABCDEF')"/>
    <xsl:param name="value" select="string-length(substring-before('0123456789ABCDEF', $MSB))"/>
    <xsl:param name="result" select="16 * $num + $value"/>
    <xsl:choose>
        <xsl:when test="string-length($hex) > 1">
            <xsl:call-template name="hex2num">
                <xsl:with-param name="hex" select="substring($hex, 2)"/>
                <xsl:with-param name="num" select="$result"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$result"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

应用于以下测试输入

<body>
    <node value="1"/>
    <node value="0x02"/>
    <node value="3"/>
    <node value="0x04"/>
    <node value="0xB1"/>
</body>

产生这个结果

<?xml version="1.0" encoding="UTF-8"?>
<body>
   <node value="3"/>
   <node value="0x04"/>
   <node value="0xB1"/>
</body>

【讨论】:

  • 不错,迈克尔。如果没有更简单的方法,我会感到失望,但通常情况下会限制为 1.0。我向你脱帽致敬。我对答案投了赞成票,因为它提供的信息非常丰富;直到下周我才能在上下文中测试它,但大概它会起作用,然后我会“接受”答案。谢谢。
  • @Captain 使用 msxsl 有一种更简单的方法来执行此操作。专有的,但更容易。看我的回答。
  • 有趣的是,我无意中注意到一位同事处理与我相同的 XML 数据集,但出于完全不同的目的,不得不解决相同的问题,使用谷歌找到了这个答案,并没有使用它知道我已经发布了问题!
【解决方案2】:

既然您说您的处理器是 MSXSL,您可以利用 msxsl 扩展,它允许您使用 define a script 来完成 XSLT 处理器本身无法完成的工作。

下面使用一个小的 JScript 函数,它将所有以 0x 开头的十六进制数字转换为对应的十进制数字。

<xsl:stylesheet
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  xmlns:script="http://tempuri.org/script"
  exclude-result-prefixes="msxsl script"
>
  <xsl:output method="xml" encoding="UTF-8" indent="yes"/>

  <xsl:template match="/body">
    <xsl:copy>
      <xsl:copy-of select="node[script:hexToDec(@value) &gt; 2]" />
    </xsl:copy>
  </xsl:template>

  <msxsl:script language="jscript" implements-prefix="script"><![CDATA[
    function hexToDec(nodeList) {
      var firstNode, matches;
      if (nodeList.length) {
        firstNode = nodeList.nextNode();
        matches = /^\s*0x0*([0-9A-F]+)\s*$/i.exec(firstNode.text);
        return matches ? parseInt(matches[1], 16) : firstNode.text;
      }
      return "";
    }
  ]]></msxsl:script>
</xsl:stylesheet>

msxsl 命名空间还允许以更高级的方式扩展 XSLT 处理器,例如使用 COM DLL 或 .NET 代码,但对于这种简单的场景,JScript 就可以了。

【讨论】:

  • 嗨@Tomalak。这是一种非常有用的方法,也是如何将脚本与 msxsl 一起使用的一个很好的示例。我不知道这是可能的。就我而言,这一次,出于几个外部原因,我将使用纯 XSLT 方法。对于具有多个需要解释/转换的属性的一般情况,脚本看起来是一种更加方便和紧凑的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
  • 2012-08-05
  • 2014-02-20
  • 1970-01-01
  • 2013-08-27
相关资源
最近更新 更多