Last active
November 30, 2021 23:02
-
-
Save jarrodek/24d32cb6a03c40b11fe77651ba223257 to your computer and use it in GitHub Desktop.
Parsing a JSON that has a big integer value
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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