How to Parse BigInt Numbers With JSON.parse() Without Losing Precision?

To parse a large number in a JSON string without losing precision, you can transform it to BigInt by specifying a reviver function as the second argument to JSON.parse().

Syntax

JSON.parse(json, reviverFn)

The reviver function is called with the following two arguments:

  1. key — the key associated with the value being parsed;
  2. value — the parsed value.

Implementation

You can use the reviver function to define how to deal with large numbers and convert them to BigInt, for example, in the following steps:

  1. Return early if value is not a string;
  2. Consider BigInt literal values with or without "n", and exclude "n" from the string if it exists;
  3. Return any non-numeric values unchanged;
  4. Convert number to:
    • BigInt if it exceeds the safe integer range (Number.MIN_SAFE_INTEGER - Number.MAX_SAFE_INTEGER), or;
    • Number if it's within safe integer range.

Code

// ES10+
const bigIntReviver = (key, value) => {
  // 1: return early if `value` is not a string
  if (typeof value !== 'string') {
    return value;
  }

  // 2.1: Consider `BigInt` values with or without 'n'
  // 2.2: Exclude `n` from the end of string, if it exists
  const match = /^(-?\d+)n?$/.exec(value);

  // 3: return non-numeric values unchanged
  if (!match) {
    return value;
  }

  const numericValue = match[1];

  // 4.1: Convert to `BigInt` if value exceeds safe integer range
  if (BigInt(numericValue) > Number.MAX_SAFE_INTEGER || BigInt(numericValue) < Number.MIN_SAFE_INTEGER) {
    return BigInt(numericValue);
  }

  // 4.2: Convert to `Number` if value is within safe integer range
  return Number(numericValue);
};

Examples

The custom bigIntReviver reviver function should handle cases where the JSON string represents a BigInt literal with or without the "n" at the end:

// ES10+
// case 1: negative/positive numeric values exceeding safe range
console.log(JSON.parse('{"num":"9223372036854775807000n"}', bigIntReviver)); // {num: 9223372036854775807000n}
console.log(JSON.parse('{"num":"-18014398509481982n"}', bigIntReviver)); // {num: -18014398509481982n}

console.log(JSON.parse('{"num":"9223372036854775807000"}', bigIntReviver)); // {num: 9223372036854775807000n}
console.log(JSON.parse('{"num":"-18014398509481982"}', bigIntReviver)); // {num: -18014398509481982n}

// case 2: negative/positive numeric values in safe range
console.log(JSON.parse('{"num":"1234n"}', bigIntReviver)); // {num: 1234}
console.log(JSON.parse('{"num":"-1234n"}', bigIntReviver)); // {num: -1234}
console.log(JSON.parse('{"num":"0n"}', bigIntReviver)); // {num: 0}
console.log(JSON.parse('{"num":"-0n"}', bigIntReviver)); // {num: -0}

console.log(JSON.parse('{"num":"1234"}', bigIntReviver)); // {num: 1234}
console.log(JSON.parse('{"num":"-1234"}', bigIntReviver)); // {num: -1234}
console.log(JSON.parse('{"num":"0"}', bigIntReviver)); // {num: 0}
console.log(JSON.parse('{"num":"-0"}', bigIntReviver)); // {num: -0}

The "n" at the end of a number merely suggests that the number is a bigint primitive.


This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.