JSPM

  • Created
  • Published
  • Downloads 22184
  • Score
    100M100P100Q134312F
  • 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 in general, especially of long strings (by using indexOf), and 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.

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, without lots of 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, which is 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 probably 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 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 are mostly from Nicolas Seriot's JSON Test Suite, which is MIT licenced.