Skip to content

Instantly share code, notes, and snippets.

@jarrodek
Last active November 30, 2021 23:02
Show Gist options
  • Save jarrodek/24d32cb6a03c40b11fe77651ba223257 to your computer and use it in GitHub Desktop.
Save jarrodek/24d32cb6a03c40b11fe77651ba223257 to your computer and use it in GitHub Desktop.
Parsing a JSON that has a big integer value
/* This is the value that is being parsed. It is defined here because the `reviver` function need the access to it. */
const body = '[{"id":1110746394641760256,"name":"fc1"}]';
/**
* The reviver function passed as a second argument to the JSON.parse() function.
* It transforms the numbers that are above the max integer value into BigInt.
*
* Note 1, BigInt is not the same as a Number. In fact you cannot perform operations on the thwo types.
* Note 2, This won't work when the stringified object has multiple number values that are similar
* (the first 10+ digits are the same, depnding on the value of the `Number.MAX_SAFE_INTEGE`).
*
* @param {any} key The deserialized object's key that is being processed
* @param {any} value The value of the kay that has already been processed.
* @returns {any} The processed value.
*/
function reviver(key, value) {
if (typeof value !== 'number' || Number.MAX_SAFE_INTEGER > value) {
// we ignore non numbers and numbers that are lower than the safe integer value.
return value;
}
// first lets find out how many digits the max integer has.
// From there we subtract one digit to be sure that we won't get the parsed value that has been rounded.
const maxLen = Number.MAX_SAFE_INTEGER.toString().length - 1;
// Next we convert the current value to string and we are getting just the potion of it that has the safe integer max size.
const needle = String(value).substr(0, maxLen);
// Now we build a regular expression that has the needle plus some digits after it. This is the number that we are looking for
// in the original string. Note, if there are many numbers that starts with the same digits this will only find the first one.
// This can be extended to cache the reular expressions and reuse already executed ones to continue searching for the
// next occurence of the number.
const re = new RegExp(`${needle}\\d+`);
const matches = body.match(re);
if (matches) {
// since we have a match we return it as a BigInt.
return BigInt(matches[0]);
}
// otherwise we returns the value that would be returned anyway.
return value;
}
const parsed = JSON.parse(body, reviver);
console.log(parsed);
// [
// {
// id: 1110746394641760256n
// name: "fc1"
// }
// ]
// Serializing it back to JSON is also tricky as BigInt has no toJSON methods that serializes it any way.
// We use method descibed in: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
BigInt.prototype.toJSON = function() { return this.toString() };
// note that this changes the global prototype of a built-in object and shoudl be avoided.
// In some restrictive environments it will be impossible to change the signature of a built-in objects (like Salesforce platform).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment