On javascript (not using node), I am facing different results when CBOR encoding using library (https://github.com/paroga/cbor-js) and when using CBOR online (https://cbor.me/). Note that even using a more recent CBOR library, result is identical.
For instance setting an object such as :
const initial = { 1: "John", "-2": 456 };
Encoding using CBOR online gives : a201644a6f686e622d321901c8. Details are :
A2 # map(2)
01 # unsigned(1)
64 # text(4)
4A6F686E # "John"
62 # text(2)
2D32 # "-2"
19 01C8 # unsigned(456)
Now encoding using CBOR library on javascript gives a different result : a26131644a6f686e622d321901c8
When decoding this above Hexadecimal on CBOR online, I got : {"1": "John", "-2": 456}. Result is almost identical than the constant 'initial' except that key 1 now appears with a quote (").
CBOR online re-formats my hexadecimal value to a more 'readable' view :
A2 # map(2)
61 # text(1)
31 # "1"
64 # text(4)
4A6F686E # "John"
62 # text(2)
2D32 # "-2"
19 01C8 # unsigned(456)
See below my Javascript code :
//convert an array of bytes (as 8 bits) to string of Hex. ensure that Hex value are not return with 1 digit but 2 digits. ie '01' instead of '1'
function toHexString(byteArray) {
var s = '';
byteArray.forEach(function(byte) {
s += ('0' + (byte & 0xFF).toString(16)).slice(-2);
});
return s;
}
const initial = { 1: "John", "-2": 456 };
var encoded = CBOR.encode(initial);
var encodedHex = toHexString(Array.from(new Uint8Array(encoded)));
console.log ( encodedHex );
I could manually replace specific hexadecimal values such as :
'61 31 64' replaced by '01 64'
But not fancy doing it as list could be important to cover all possible options.
Does someone have a workaround as I need my result to be 'a201644a6f686e622d321901c8' and not 'a26131644a6f686e622d321901c8' ?
The CBOR specification, section 5.6 says:
In applications that need to interwork with JSON-based applications, conversion is simplified by limiting keys to text strings only
And indeed, the cbor-js package uses the Object.keys method here, which returns all keys as strings. Javascript does not distinguish between numbers and their string values and treats {'1':1, 1:2} as {'1':2} (whereas cbor.me treats this as a map with two entries).
cbor-jsYour example suggests that you want non-negative numeric keys treated as numeric by CBOR. This can be achieved with the following patch on the cbor-js source code:
diff --git a/cbor.js b/cbor.js
--- a/cbor.js
+++ b/cbor.js
@@ -164,7 +164,10 @@ function encode(value) {
writeTypeAndLength(5, length);
for (i = 0; i < length; ++i) {
var key = keys[i];
- encodeItem(key);
+ if (isNaN(key) || Number(key) < 0)
+ encodeItem(key);
+ else
+ encodeItem(Number(key));
encodeItem(value[key]);
}
}
With this change, Node.js gives me
> Buffer.from(cbor.encode({1:'John','-2':456})).toString('hex'))
'a201644a6f686e622d321901c8'
Or you could even treat negative numeric keys as numeric by leaving out the || Number(key) < 0 in the patch above. This gives
> Buffer.from(cbor.encode({1:'John','-2':456})).toString('hex'))
'a201644a6f686e211901c8'
A2 # map(2)
01 # unsigned(1)
64 # text(4)
4A6F686E # "John"
21 # negative(1)
19 01C8 # unsigned(456)
cborUnlike cbor-js, the cbor package allows you to encode a Javascript Map, which distinguishes numeric from string keys:
> Buffer.from(cbor.encode(new Map().set(1,'John').set(-2,456))).toString('hex')
'a201644a6f686e211901c8'