diff options
Diffstat (limited to '')
-rw-r--r-- | _tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/_tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md b/_tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md new file mode 100644 index 0000000..f309641 --- /dev/null +++ b/_tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md @@ -0,0 +1,107 @@ +--- + +title: Encoding and decoding JavaScript BigInt values with reviver + +date: 2021-08-11 + +layout: post + +lang: en + +ref: encoding-and-decoding-javascript-bigint-values-with-reviver + +--- + +`JSON.parse()` accepts a second parameter: a [`reviver()` function][reviver]. +It is a function that can be used to transform the `JSON` values as they're +being parsed. + +[reviver]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#using_the_reviver_parameter + +As it turns out, when combined with JavaScript's [`BigInt`] type, you can parse +and encode JavaScript `BigInt` numbers via JSON: + +[`BigInt`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt + +```javascript +const bigIntReviver = (_, value) => { + if ( + typeof value === "string" + && value[value.length - 1] === "n" + && value.length > 1 + ) { + const numberStr = value.slice(0, value.length - 1); + if (numberStr.match(/^[0-9]+$/) !== null) { + return BigInt(numberStr); + } + } + return value; +}; +``` + +I chose to interpret strings that contains only numbers and an ending `n` suffix +as `BigInt` values, similar to how JavaScript interprets `123` (a number) +differently from `123n` (a `bigint`); + +We do those checks before constructing the `BigInt` to avoid throwing needless +exceptions on the parsing function, as this could easily become a bottleneck +when parsing large JSON values. + +In order to do the full roundtrip, we now only need the `toJSON()` counterpart: + +```javascript +BigInt.prototype.toJSON = function() { + return this.toString() + "n"; +}; +``` + +With both `bigIntReviver` and `toJSON` defined, we can now successfully parse +and encode JavaScript objects with `BigInt` values transparently: + +```javascript +const s = `[ + null, + true, + false, + -1, + 3.14, + "a string", + { "a-number": "123" }, + { "a-bigint": "123n" } +]`; + +const parsed = JSON.parse(s, bigIntReviver); +const s2 = JSON.stringify(parsed); + +console.log(parsed); +console.log(s2); + +console.log(typeof parsed[6]["a-number"]) +console.log(typeof parsed[7]["a-bigint"]) +``` + +The output of the above is: + +``` +[ + null, + true, + false, + -1, + 3.14, + 'a string', + { 'a-number': '123' }, + { 'a-bigint': 123n } +] +[null,true,false,-1,3.14,"a string",{"a-number":"123"},{"a-bigint":"123n"}] +string +bigint +``` + +If you're on a web browser, you can probably try copying and pasting the above +code on the console right now, as is. + +Even though [`JSON`] doesn't include `BigInt` number, encoding and decoding them +as strings is quite trivial on JavaScript. + +[`JSON`]: https://datatracker.ietf.org/doc/html/rfc8259 |