Skip to content

Commit

Permalink
Merge pull request #36 from babyadoresorange/master
Browse files Browse the repository at this point in the history
use native BigInt as option, add always option
  • Loading branch information
sidorares committed Jul 12, 2020
2 parents a544c58 + 1556563 commit 6c659f5
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 9 deletions.
76 changes: 75 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ json-bigint
[![Build Status](https://secure.travis-ci.org/sidorares/json-bigint.png)](http://travis-ci.org/sidorares/json-bigint)
[![NPM](https://nodei.co/npm/json-bigint.png?downloads=true&stars=true)](https://nodei.co/npm/json-bigint/)

JSON.parse/stringify with bigints support. Based on Douglas Crockford [JSON.js](https://github.com/douglascrockford/JSON-js) package and [bignumber.js](https://github.com/MikeMcl/bignumber.js) library.
JSON.parse/stringify with bigints support. Based on Douglas Crockford [JSON.js](https://github.com/douglascrockford/JSON-js) package and [bignumber.js](https://github.com/MikeMcl/bignumber.js) library.

Native `Bigint` was added to JS recently, so we added an option to leverage it instead of `bignumber.js`. However, the parsing with native `BigInt` is kept an option for backward compability.

While most JSON parsers assume numeric values have same precision restrictions as IEEE 754 double, JSON specification _does not_ say anything about number precision. Any floating point number in decimal (optionally scientific) notation is valid JSON value. It's a good idea to serialize values which might fall out of IEEE 754 integer precision as strings in your JSON api, but `{ "value" : 9223372036854775807}`, for example, is still a valid RFC4627 JSON string, and in most JS runtimes the result of `JSON.parse` is this object: `{ value: 9223372036854776000 }`

Expand Down Expand Up @@ -107,6 +109,61 @@ Default type: object, With option type: string
```

#### options.useNativeBigInt, boolean, default false
Specifies if parser uses native BigInt instead of bignumber.js

example:
```js
var JSONbig = require('json-bigint');
var JSONbigNative = require('json-bigint')({"useNativeBigInt": true});
var key = '{ "key": 993143214321423154315154321 }';
console.log(`\n\nStoring the Number as native BigInt, instead of a BigNumber`);
console.log('Input:', key);
var normal = JSONbig.parse(key);
var nativeBigInt = JSONbigNative.parse(key);
console.log('Default type: %s, With option type: %s', typeof normal.key, typeof nativeBigInt.key);

```

Output
```
Storing the Number as native BigInt, instead of a BigNumber
Input: { "key": 993143214321423154315154321 }
Default type: object, With option type: bigint
```

#### options.alwaysParseAsBig, boolean, default false
Specifies if all numbers should be stored as BigNumber.

Note that this is a dangerous behavior as it breaks the default functionality of being able to convert back-and-forth without data type changes (as this will convert all Number to be-and-stay BigNumber)

example:
```js
var JSONbig = require('json-bigint');
var JSONbigAlways = require('json-bigint')({"alwaysParseAsBig": true});
var key = '{ "key": 123 }'; // there is no need for BigNumber by default, but we're forcing it
console.log(`\n\nStoring the Number as a BigNumber, instead of a Number`);
console.log('Input:', key);
var normal = JSONbig.parse(key);
var always = JSONbigAlways.parse(key);
console.log('Default type: %s, With option type: %s', typeof normal.key, typeof always.key);

```

Output
```
Storing the Number as a BigNumber, instead of a Number
Input: { "key": 123 }
Default type: number, With option type: object
```

If you want to force all numbers to be parsed as native `BigInt`
(you probably do! Otherwise any calulations become a real headache):
```js
var JSONbig = require('json-bigint')({"alwaysParseAsBig": true, "useNativeBigInt": true});
```

### Links:
- [RFC4627: The application/json Media Type for JavaScript Object Notation (JSON)](http://www.ietf.org/rfc/rfc4627.txt)
Expand All @@ -115,3 +172,20 @@ Default type: object, With option type: string
- [What is JavaScript's Max Int? What's the highest Integer value a Number can go to without losing precision?](http://stackoverflow.com/questions/307179/what-is-javascripts-max-int-whats-the-highest-integer-value-a-number-can-go-t)
- [Large numbers erroneously rounded in Javascript](http://stackoverflow.com/questions/1379934/large-numbers-erroneously-rounded-in-javascript)

### Note on native BigInt support

#### Stringifying
Full support out-of-the-box, stringifies BigInts as pure numbers (no quotes, no `n`)

#### Limitations
- Roundtrip operations

`s === JSONbig.stringify(JSONbig.parse(s))` but

`o !== JSONbig.parse(JSONbig.stringify(o))`

when `o` has a value with something like `123n`.

`JSONbig` stringify `123n` as `123`, which becomes `number` (aka `123` not `123n`) by default when being reparsed.

There is currently no consistent way to deal with this issue, so we decided to leave it, handling this specific case is then up to users.
19 changes: 14 additions & 5 deletions lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ var json_parse = function (options) {
// Default options one can override by passing options to the parse()
var _options = {
"strict": false, // not being strict means do not generate syntax errors for "duplicate key"
"storeAsString": false // toggles whether the values should be stored as BigNumber (default) or a string
"storeAsString": false, // toggles whether the values should be stored as BigNumber (default) or a string
"alwaysParseAsBig": false, // toggles whether all numbers should be Big
"useNativeBigInt": false // toggles whether to use native BigInt instead of bignumber.js
};


Expand All @@ -88,6 +90,8 @@ var json_parse = function (options) {
if (options.storeAsString === true) {
_options.storeAsString = true;
}
_options.alwaysParseAsBig = options.alwaysParseAsBig === true ? options.alwaysParseAsBig : false
_options.useNativeBigInt = options.useNativeBigInt === true ? options.useNativeBigInt : false
}


Expand Down Expand Up @@ -174,8 +178,9 @@ var json_parse = function (options) {
//if (number > 9007199254740992 || number < -9007199254740992)
// Bignumber has stricter check: everything with length > 15 digits disallowed
if (string.length > 15)
return (_options.storeAsString === true) ? string : new BigNumber(string);
return number;
return _options.storeAsString ? string : _options.useNativeBigInt ? BigInt(string) : new BigNumber(string);
else
return !_options.alwaysParseAsBig ? number : _options.useNativeBigInt ? BigInt(number) : new BigNumber(number);
}
},

Expand All @@ -191,12 +196,17 @@ var json_parse = function (options) {
// When parsing for string values, we must look for " and \ characters.

if (ch === '"') {
var startAt = at;
while (next()) {
if (ch === '"') {
if (at - 1 > startAt)
string += text.substring(startAt, at - 1);
next();
return string;
}
if (ch === '\\') {
if (at - 1 > startAt)
string += text.substring(startAt, at - 1);
next();
if (ch === 'u') {
uffff = 0;
Expand All @@ -213,8 +223,7 @@ var json_parse = function (options) {
} else {
break;
}
} else {
string += ch;
startAt = at;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/stringify.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ var JSON = module.exports;

case 'boolean':
case 'null':
case 'bigint':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
Expand Down
167 changes: 167 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "json-bigint",
"version": "0.3.0",
"version": "0.4.0",
"description": "JSON.parse with bigints support",
"main": "index.js",
"scripts": {
Expand All @@ -26,4 +26,4 @@
"chai": "~1.9.1",
"mocha": "~1.20.1"
}
}
}

0 comments on commit 6c659f5

Please sign in to comment.