aboutsummaryrefslogblamecommitdiff
path: root/_tils/2021-08-11-encoding-and-decoding-javascript-bigint-values-with-reviver.md
blob: f309641d91bdb1a12c8f99636254f07836f3c793 (plain) (tree)










































































































                                                                                                                                  
---

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