Reputation: 471
I need to compare XML attributes which represent integers, but may be given in decimal or hex (with 0x prefix), using XPath/XSLT-1.0.
Here is a (not working) XSLT to demonstrate:
<?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 > 2">
<xsl:value-of select="@value"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Here is an XML to input:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<node value="1"/>
<node value="3"/>
<node value="0x03"/>
</body>
Here is the desired output. The format is not important; important is only that there is a match on the second and third nodes:
3 0x03
There is only a match on the second node; the hex node is not interpreted as a number by XML. Can anyone think of a reasonable solution to this problem?
Upvotes: 1
Views: 2131
Reputation: 338376
Since you said that your processor is MSXSL, you can tap into the msxsl
extensions, which allow you to define a script that you can use for work that the XSLT processor itself cannot do.
The following uses a small JScript function that converts all hexadecimal numbers that start with 0x
to their decimal counterpart.
<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) > 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>
The msxsl
namespace also allows more advanced ways of extending the XSLT processor, for example with COM DLLs or .NET code, but for this simple scenario JScript does just fine.
Upvotes: 1
Reputation: 117140
The format is not important;
Then for convenience I will demonstrate with an XML format as the output:
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>
Applied to the following test input:
<body>
<node value="1"/>
<node value="0x02"/>
<node value="3"/>
<node value="0x04"/>
<node value="0xB1"/>
</body>
produces this result:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<node value="3"/>
<node value="0x04"/>
<node value="0xB1"/>
</body>
Upvotes: 4