Reputation: 115
I have some numbers in json which overflow the Number type, so I want it to be bigint, but how?
{"foo":[[0],[64],[89],[97]],"bar":[[2323866757078990912,144636906343245838,441695983932742154,163402272522524744],[2477006750808014916,78818525534420994],[18577623609266200],[9008333127155712]]}
Upvotes: 8
Views: 22034
Reputation: 31
You can try my library that solves this problem: https://www.npmjs.com/package/json-with-bigint
Example:
import { JSONParse } from 'json-with-bigint';
const yourJSON = `{"someValue":42,"someBigValue":10000000000000000365}`;
JSONParse(yourJSON); // { someValue: 42, someBigValue: 10000000000000000365n }
Library will automatically figure out what values are BigInt
, so no need to pass a list of specific keys with BigInt
values.
It also supports consistent round-trip operations (parse - stringify - parse). Deserialized values will be the same as the ones you serialized initially and vice versa.
import { JSONParse, JSONStringify } from 'json-with-bigint';
const yourJSON = `{"someValue":42,"someBigValue":10000000000000000365}`;
JSONParse(yourJSON); // { someValue: 42, someBigValue: 10000000000000000365n }
JSONStringify(data); // '{"someValue":42,"someBigValue":10000000000000000365}'
JSONParse(JSONStringify(data)); // { someValue: 42, someBigValue: 10000000000000000365n }
Upvotes: 3
Reputation: 15530
You may employ JSON.parse()
reviver parameter
To control JSON.parse()
behavior that way, you can make use of the second parameter of JSON.parse
(reviver
) - the function that pre-processes key-value pairs (and may potentially pass desired values to BigInt()
).
Yet, the values recognized as numbers will still be coerced (the credit for pinpointing this issue goes to @YohanesGultom).
To get around this, you may enquote your big numbers (to turn them into strings) in your source JSON string, so that their values are preserved upon converting to bigint
.
As long as you wish to convert to bigint
only certain numbers, you would need to pick up appropriate criteria (e.g. to check whether the value exceeds Number.MAX_SAFE_INTEGER
with Number.isSafeInteger()
, as @PeterSeliger has suggested).
Thus, your problem may be solved with something, like this:
// source JSON string
const input = `{"foo":[[0],[64],[89],[97]],"bar":[[2323866757078990912,144636906343245838,441695983932742154,163402272522524744],[2477006750808014916,78818525534420994],[18577623609266200],[9008333127155712]]}`
// function that implements desired criteria
// to separate *big numbers* from *small* ones
//
// (works for input parameter num of type number/string)
const isBigNumber = num => !Number.isSafeInteger(+num)
// function that enquotes *big numbers* matching
// desired criteria into double quotes inside
// JSON string
//
// (function checking for *big numbers* may be
// passed as a second parameter for flexibility)
const enquoteBigNumber = (jsonString, bigNumChecker) =>
jsonString
.replaceAll(
/([:\s\[,]*)(\d+)([\s,\]]*)/g,
(matchingSubstr, prefix, bigNum, suffix) =>
bigNumChecker(bigNum)
? `${prefix}"${bigNum}"${suffix}`
: matchingSubstr
)
// parser that turns matching *big numbers* in
// source JSON string to bigint
const parseWithBigInt = (jsonString, bigNumChecker) =>
JSON.parse(
enquoteBigNumber(jsonString, bigNumChecker),
(key, value) =>
!isNaN(value) && bigNumChecker(value)
? BigInt(value)
: value
)
// resulting output
const output = parseWithBigInt(input, isBigNumber)
console.log("output.foo[1][0]: \n", output.foo[1][0], `(type: ${typeof output.foo[1][0]})`)
console.log("output.bar[0][0]: \n", output.bar[0][0].toString(), `(type: ${typeof output.bar[0][0]})`)
.as-console-wrapper{min-height: 100% !important;}
Note: you may find RegExp pattern to match strings of digits among JSON values not quite robust, so feel free to come up with yours (as mine was the quickest I managed to pick off the top of my head for demo purposes)
Note: you may still opt in for some library, as it was suggested by @YohanesGultom, yet adding 10k to your client bundle or 37k to your server-side dependencies (possibly, to docker image size) for that sole purpose may not be quite reasonable.
Upvotes: 17