You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					241 lines
				
				8.6 KiB
			
		
		
			
		
	
	
					241 lines
				
				8.6 KiB
			| 
											2 years ago
										 | # json-bigint
 | ||
|  | 
 | ||
|  | [](http://travis-ci.org/sidorares/json-bigint) | ||
|  | [](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. | ||
|  | 
 | ||
|  | 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 }` | ||
|  | 
 | ||
|  | ========== | ||
|  | 
 | ||
|  | example: | ||
|  | 
 | ||
|  | ```js | ||
|  | var JSONbig = require('json-bigint'); | ||
|  | 
 | ||
|  | var json = '{ "value" : 9223372036854775807, "v2": 123 }'; | ||
|  | console.log('Input:', json); | ||
|  | console.log(''); | ||
|  | 
 | ||
|  | console.log('node.js built-in JSON:'); | ||
|  | var r = JSON.parse(json); | ||
|  | console.log('JSON.parse(input).value : ', r.value.toString()); | ||
|  | console.log('JSON.stringify(JSON.parse(input)):', JSON.stringify(r)); | ||
|  | 
 | ||
|  | console.log('\n\nbig number JSON:'); | ||
|  | var r1 = JSONbig.parse(json); | ||
|  | console.log('JSONbig.parse(input).value : ', r1.value.toString()); | ||
|  | console.log('JSONbig.stringify(JSONbig.parse(input)):', JSONbig.stringify(r1)); | ||
|  | ``` | ||
|  | 
 | ||
|  | Output: | ||
|  | 
 | ||
|  | ``` | ||
|  | Input: { "value" : 9223372036854775807, "v2": 123 } | ||
|  | 
 | ||
|  | node.js built-in JSON: | ||
|  | JSON.parse(input).value :  9223372036854776000 | ||
|  | JSON.stringify(JSON.parse(input)): {"value":9223372036854776000,"v2":123} | ||
|  | 
 | ||
|  | 
 | ||
|  | big number JSON: | ||
|  | JSONbig.parse(input).value :  9223372036854775807 | ||
|  | JSONbig.stringify(JSONbig.parse(input)): {"value":9223372036854775807,"v2":123} | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Options
 | ||
|  | 
 | ||
|  | The behaviour of the parser is somewhat configurable through 'options' | ||
|  | 
 | ||
|  | #### options.strict, boolean, default false
 | ||
|  | 
 | ||
|  | Specifies the parsing should be "strict" towards reporting duplicate-keys in the parsed string. | ||
|  | The default follows what is allowed in standard json and resembles the behavior of JSON.parse, but overwrites any previous values with the last one assigned to the duplicate-key. | ||
|  | 
 | ||
|  | Setting options.strict = true will fail-fast on such duplicate-key occurances and thus warn you upfront of possible lost information. | ||
|  | 
 | ||
|  | example: | ||
|  | 
 | ||
|  | ```js | ||
|  | var JSONbig = require('json-bigint'); | ||
|  | var JSONstrict = require('json-bigint')({ strict: true }); | ||
|  | 
 | ||
|  | var dupkeys = '{ "dupkey": "value 1", "dupkey": "value 2"}'; | ||
|  | console.log('\n\nDuplicate Key test with both lenient and strict JSON parsing'); | ||
|  | console.log('Input:', dupkeys); | ||
|  | var works = JSONbig.parse(dupkeys); | ||
|  | console.log('JSON.parse(dupkeys).dupkey: %s', works.dupkey); | ||
|  | var fails = 'will stay like this'; | ||
|  | try { | ||
|  |   fails = JSONstrict.parse(dupkeys); | ||
|  |   console.log('ERROR!! Should never get here'); | ||
|  | } catch (e) { | ||
|  |   console.log( | ||
|  |     'Succesfully catched expected exception on duplicate keys: %j', | ||
|  |     e | ||
|  |   ); | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Output | ||
|  | 
 | ||
|  | ``` | ||
|  | Duplicate Key test with big number JSON | ||
|  | Input: { "dupkey": "value 1", "dupkey": "value 2"} | ||
|  | JSON.parse(dupkeys).dupkey: value 2 | ||
|  | Succesfully catched expected exception on duplicate keys: {"name":"SyntaxError","message":"Duplicate key \"dupkey\"","at":33,"text":"{ \"dupkey\": \"value 1\", \"dupkey\": \"value 2\"}"} | ||
|  | 
 | ||
|  | ``` | ||
|  | 
 | ||
|  | #### options.storeAsString, boolean, default false
 | ||
|  | 
 | ||
|  | Specifies if BigInts should be stored in the object as a string, rather than the default 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 BigInts to be-and-stay strings). | ||
|  | 
 | ||
|  | example: | ||
|  | 
 | ||
|  | ```js | ||
|  | var JSONbig = require('json-bigint'); | ||
|  | var JSONbigString = require('json-bigint')({ storeAsString: true }); | ||
|  | var key = '{ "key": 1234567890123456789 }'; | ||
|  | console.log('\n\nStoring the BigInt as a string, instead of a BigNumber'); | ||
|  | console.log('Input:', key); | ||
|  | var withInt = JSONbig.parse(key); | ||
|  | var withString = JSONbigString.parse(key); | ||
|  | console.log( | ||
|  |   'Default type: %s, With option type: %s', | ||
|  |   typeof withInt.key, | ||
|  |   typeof withString.key | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | Output | ||
|  | 
 | ||
|  | ``` | ||
|  | Storing the BigInt as a string, instead of a BigNumber | ||
|  | Input: { "key": 1234567890123456789 } | ||
|  | 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, | ||
|  | }); | ||
|  | ``` | ||
|  | 
 | ||
|  | #### options.protoAction, boolean, default: "error". Possible values: "error", "ignore", "preserve"
 | ||
|  | 
 | ||
|  | #### options.constructorAction, boolean, default: "error". Possible values: "error", "ignore", "preserve"
 | ||
|  | 
 | ||
|  | Controls how `__proto__` and `constructor` properties are treated. If set to "error" they are not allowed and | ||
|  | parse() call will throw an error. If set to "ignore" the prroperty and it;s value is skipped from parsing and object building. | ||
|  | If set to "preserve" the `__proto__` property is set. One should be extra careful and make sure any other library consuming generated data | ||
|  | is not vulnerable to prototype poisoning attacks. | ||
|  | 
 | ||
|  | example: | ||
|  | 
 | ||
|  | ```js | ||
|  | var JSONbigAlways = require('json-bigint')({ protoAction: 'ignore' }); | ||
|  | const user = JSONbig.parse('{ "__proto__": { "admin": true }, "id": 12345 }'); | ||
|  | // => result is { id: 12345 } | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Links:
 | ||
|  | 
 | ||
|  | - [RFC4627: The application/json Media Type for JavaScript Object Notation (JSON)](http://www.ietf.org/rfc/rfc4627.txt) | ||
|  | - [Re: \[Json\] Limitations on number size?](http://www.ietf.org/mail-archive/web/json/current/msg00297.html) | ||
|  | - [Is there any proper way to parse JSON with large numbers? (long, bigint, int64)](http://stackoverflow.com/questions/18755125/node-js-is-there-any-proper-way-to-parse-json-with-large-numbers-long-bigint) | ||
|  | - [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. |