JSPM

  • Created
  • Published
  • Downloads 22242
  • Score
    100M100P100Q142929F
  • License MIT

JSON parser enabling custom number parsing

Package Exports

  • json-custom-numbers

Readme

JSON custom numbers

https://github.com/jawj/json-custom-numbers

This is a heavily modified version of Douglas Crockford's recursive-descent JSON parser.

These modifications:

  • enable custom number parsing, via a function you supply at parse time

  • accelerate parsing, especially of long strings (by using indexOf), and in general by inlining short functions

  • match JSON.parse behaviour by allowing duplicate object keys (last value wins), and by being strict about whitespace characters, number formats (.5, 5. and 05 are errors), unicode escapes, and (optionally) unescaped \t, \n and control characters in strings.

Conformance and compatibility

When full string checking is not explicitly turned off, the parse() function matches the behaviour of JSON.parse() for every test in the JSON Parsing Test Suite, and a few more.

Performance

Performance comparisons are very dependent on the nature of the JSON string to be parsed and the JavaScript engine used.

On Node.js 18.10 and 20.0 (V8 engine):

  • The best case is JSON that contains mainly long strings with few escape sequences. This library may then be over 10x faster than JSON.parse(), if full string checking is turned off. With full string checking turned on (the default), it's still around 2x faster.

  • The worst case is JSON that contains only short numeric values, true, false or null. This library may then be 4 - 5x slower than JSON.parse().

  • Typically, this library is 2 – 3x slower than JSON.parse(). Unless you're regularly parsing very large JSON strings, the difference probably isn't very important.

On Bun 0.6.1 (JavaScriptCore engine):

  • Performance on Bun is somwehat more consistent across different JSON strings than Node. It's typically 2 - 4x slower than JSON.parse.

I compared several alternative approaches to number and string parsing. The implementations currently used are the ones I found to be fastest in most scenarios. If you figure out something reliably faster, I'd be glad to hear about it.

Usage

For usage, see the type definitions.

Number parsing

A key application of this library is converting large integers in JSON (e.g. from Postgres query results) to BigInts.

// `JSON.parse` loses precision for large integers
JSON.parse("9007199254740991"); // => 9007199254740991
JSON.parse("9007199254740993"); // => 9007199254740992

// without a `numberReviver` function, our behaviour is identical
parse("9007199254740991"); // => 9007199254740991
parse("9007199254740993"); // => 9007199254740992

// this `numberReviver` function converts only large integers to `BigInt`
function nr(s) {
  const n = +s;
  if (n >= Number.MIN_SAFE_INTEGER && n <= Number.MAX_SAFE_INTEGER) return n;
  if (s.indexOf('.') !== -1 || s.indexOf('e') !== -1 && s.indexOf('E') !== -1) return n;
  return BigInt(s);
}
parse("9007199254740991", null, nr);  // => 9007199254740991
parse("9007199254740993", null, nr);  // => 9007199254740993n

Licence

Public Domain.

NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

Except: tests in the test_parsing folder that start with y_, n_ or i_ are from Nicolas Seriot's JSON Test Suite, which is MIT licenced.