I have an existing data set that utilizes an integer to store multiple values; the legacy front end did a simple bitwise check (e.g. in C#: iValues & 16 == 16) to see if a particular value was set. Is it possible to do bitwise operations in XSL, and more explicitly, to do bit level comparisons via masking? The built-in "and" will always result in "true" or "false", but perhaps it's possible via the math operators available?
I'm currently using .NET 2.0, which uses XSLT 1.0.
I haven't seen anything like this in XSLT / XPath. But I've found someone implementing this kind of operations manually. Maybe you could use the same approach, if you really need to.
XSLT is Turing-complete, see for example here or here, hence it can be done. But I have used XSLT only one or two times and can give no solution.
UPDATE
I just read a tutorial again and found a solution using the following fact. bitset(x, n)
returns true, if the n
-th bit of x
is set, false otherwise.
bitset(x, n) := floor(x / 2^n) mod 2 == 1
The following XSLT
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<table border="1" style="text-align:center;">
<tr bgcolor="#9acd32">
<th>Number</th>
<th>Bit 3</th>
<th>Bit 2</th>
<th>Bit 1</th>
<th>Bit 0</th>
</tr>
<xsl:for-each select="numbers/number">
<tr>
<td>
<xsl:value-of select="."/>
</td>
<td>
<xsl:choose>
<xsl:when test="floor(. div 8) mod 2 = 1">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:choose>
<xsl:when test="floor(. div 4) mod 2 = 1">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:choose>
<xsl:when test="floor(. div 2) mod 2 = 1">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:choose>
<xsl:when test="floor(. div 1) mod 2 = 1">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
will turn this XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<numbers>
<number>0</number>
<number>1</number>
<number>2</number>
<number>3</number>
<number>4</number>
<number>5</number>
<number>6</number>
<number>7</number>
<number>8</number>
<number>9</number>
<number>10</number>
<number>11</number>
<number>12</number>
<number>13</number>
<number>14</number>
<number>15</number>
</numbers>
into a HTML document with a table showing the bits of the numbers.
Number | Bit 3 | Bit 2 | Bit 1 | Bit 0
---------------------------------------
0 | 0 | 0 | 0 | 0
1 | 0 | 0 | 0 | 1
2 | 0 | 0 | 1 | 0
3 | 0 | 0 | 1 | 1
4 | 0 | 1 | 0 | 0
5 | 0 | 1 | 0 | 1
6 | 0 | 1 | 1 | 0
7 | 0 | 1 | 1 | 1
8 | 1 | 0 | 0 | 0
9 | 1 | 0 | 0 | 1
10 | 1 | 0 | 1 | 0
11 | 1 | 0 | 1 | 1
12 | 1 | 1 | 0 | 0
13 | 1 | 1 | 0 | 1
14 | 1 | 1 | 1 | 0
15 | 1 | 1 | 1 | 1
This is neither elegant nor nice in any way and there are probably much simpler solution, but it works. And given that it is my first contact with XSLT, I am quite satisfied.
XSLT does not define bitwise operations. If you want them, you have to roll your own.
If you're using XSLT specifically in .NET 2.0 context - that is, XslCompiledTransform
class - then the simplest solution is to use a scripting block to introduce a C# function that does it, and then just call that:
<xsl:stylesheet xmlns:bitwise="urn:bitwise">
<msxsl:script language="CSharp" implements-prefix="bitwise">
<![CDATA[
public int and(int x, int y) { return x & y; }
public int or(int x, int y) { return x | y; }
...
]]>
</msxsl:script>
...
<xsl:value-of select="bitwise:and(@foo, @bar)" />
<xsl:value-of select="bitwise:or(@foo, @bar)" />
...
</xsl:stylesheet>
Or you can define the more high-level primitives in a scripting block, such as HasFlag
, and then use those.
When loading such a stylesheet, you will need to explicitly enable scripting in it:
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load("foo.xsl",
new XsltSettings { EnableScript = true },
new XmlUrlResolver());
<xsl:value-of select="for $n in (128, 64, 32, 16, 8, 4, 2, 1) return if ((floor($var div $n) mod 2) = 1) then 1 else 0"/>
this will return the binary array of your variable (stored in $var)
by the way I used XPath 2.0 to do this