I am working on retiring a Python Kafka Consumer to use Mule runtime instead.
I have ran into a blocker with DataWeave and bitwise operators, specifically how to handle negative hex numbers.
Currently, my Kafka/Debezium producer is providing the following value +PB34g==
which should decode to -118.458398
With Python I can use Two's Complement to shift and convert to negative. But, I haven't found a comparable DataWeave solution.
We decode the Base64 value with this function, and then convert to integer with the function below.
# decode the integer
# and handle negative values
def _b64_to_int(self, value):
num = int.from_bytes(base64.b64decode(value), 'big')
# now check the sign
# and if negative, take 2's complement
# TODO: works, but isn't 64-bit safe
if num & (1 << 31):
num -= int('1' + '00' * 4, 16)
#print(f"{value} = {num}")
return num
I can handle this in a Java class, but would prefer to use DataWeave if possible.
# and handle negative values
def _b64_to_int(self, value):
num = int.from_bytes(base64.b64decode(value), 'big')
# now check the sign
# and if negative, take 2's complement
# TODO: works, but isn't 64-bit safe
if num & (1 << 31):
num -= int('1' + '00' * 4, 16)
#print(f"{value} = {num}")
return num
I have the following DataWeave to convert from Base64 to a number. I have found this article where custom bitwise functions were created, but they did not handle negative values.
%dw 2.0
output application/json
import * from dw::core::Binaries
fun to_number(b64) = dw::core::Numbers::fromHex(toHex(fromBase64(b64)))
fun to_decimal(num, scale) = to_number(num) as Number / pow(10, scale)
---
to_decimal("+PB34g==", 5)
I translated the logic from the Python snippet to DataWeave and added the to_decimal()
function from your DataWeave script to get the expected result.
WARNING: I haven't validated the logic nor the mathematics of it.
%dw 2.0
output application/json
import * from dw::core::Binaries
import * from dw::core::Numbers
var leftshift_1_31=fromBinary("10000000000000000000000000000000")
var hex_100000000="0100000000"
fun AND(bin1, bin2) = do {
var b1=getBinary(bin1, 64)
var b2=getBinary(bin2, 64)
---
fromBinary(b1 map ($ as Number * b2[$$] as Number) reduce ($$++$))
}
fun getBinary(i: Number, size: Number) = do {
var b = toBinary(i)
---
dw::core::Strings::leftPad(b, size, '0') splitBy ''
}
fun to_number(b64) = do {
var result = dw::core::Numbers::fromHex(toHex(fromBase64(b64)))
---
if ((AND(result, leftshift_1_31)==0))
result
else
result - dw::core::Numbers::fromHex(hex_100000000)
}
fun to_decimal(num, scale) = to_number(num) as Number / pow(10, scale)
---
to_decimal("+PB34g==", 6)
Output:
-118.458398