diff --git a/README.md b/README.md index 17421b8..1d3734e 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ big number JSON: JSON.parse(input).value : 9223372036854775807 JSON.stringify(JSON.parse(input)): {"value":9223372036854775807,"v2":123} ``` + ### Options The behaviour of the parser is somewhat configurable through 'options' @@ -54,6 +55,7 @@ The default follows what is allowed in standard json and resembles the behavior 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}); @@ -73,6 +75,7 @@ try { ``` Output + ``` Duplicate Key test with big number JSON Input: { "dupkey": "value 1", "dupkey": "value 2"} @@ -87,6 +90,7 @@ Specifies if BigInts should be stored in the object as a string, rather than the 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}); @@ -100,6 +104,7 @@ console.log('Default type: %s, With option type: %s', typeof withInt.key, typeof ``` Output + ``` Storing the BigInt as a string, instead of a BigNumber Input: { "key": 1234567890123456789 } @@ -107,6 +112,45 @@ Default type: object, With option type: string ``` +#### options.floatHints, boolean, default false + +Interpret the presence of a decimal point as a loose hint that this number should be treated as a float. + +If there is a mantissa and it is non-zero, then the number is parsed as a float (with precision maintained or not as defined by the in-built javascript float interpreter). e.g. `'123.45'` will parse as `123.45` and `12345678901234567890123.456` will convert to `1.2345678901234568e+22`. + +If there is a mantissa and it is only a series of zeros, then it is stripped and only the integer part is used to calculate whether a BigNumber conversion is required. i.e. `'123.000'` will parse as `123` and `12345678901234567890123.000` will convert to `BigNumber('12345678901234567890123')`. + + + +#### options.strictFloatHints, boolean, default false + +Always interpret the presence of a decimal point as an instruction to parse the number using the in-built javascript float interpreter. + +example: + +```js +var JSONbig = require('json-bigint'); +var JSONbigFloats = require('json-bigint')({"strictFloatHints": true}); +var key = '{ "key": '1234567890123456789012345.000' }'; +console.log('\n\nAlways interpreting decimal numbers as floats, instead of a BigNumber'); +console.log('Input:', key); +var withStrictFloatHints = JSONbigFloats.parse(key); +var asNormal = JSONbig.parse(key); +console.log('Default type: %s, With option type: %s', typeof asNormal.key, typeof withStrictFloatHints.key); + +``` + +Output + +``` +Always interpreting decimal numbers as floats, instead of a BigNumber +Input: { "key": "1234567890123456789012345.000" } +Default type: object, With option type: number + +``` + + + ### Links: - [RFC4627: The application/json Media Type for JavaScript Object Notation (JSON)](http://www.ietf.org/rfc/rfc4627.txt) diff --git a/lib/parse.js b/lib/parse.js index 2941c81..8b32e6c 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -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 + "floatHints": false, // toggles whether to parse numbers with non-zero mantissas as floats + "strictFloatHints": false // toggles whether to always parse numbers with mantissas as floats }; @@ -88,6 +90,12 @@ var json_parse = function (options) { if (options.storeAsString === true) { _options.storeAsString = true; } + if(options.floatHints === true) { + _options.floatHints = true; + } + if(options.strictFloatHints === true) { + _options.strictFloatHints = true; + } } @@ -171,10 +179,18 @@ var json_parse = function (options) { } else { if (BigNumber == null) BigNumber = require('bignumber.js'); + + if (_options.strictFloatHints && string.indexOf('.') !== -1) { + return parseFloat(number); + } + + var testForBigNum = (_options.floatHints) ? !/\..*[^0]/.test(string) : true; + //if (number > 9007199254740992 || number < -9007199254740992) // Bignumber has stricter check: everything with length > 15 digits disallowed - if (string.length > 15) + if (testForBigNum && string.length > 15 ) { return (_options.storeAsString === true) ? string : new BigNumber(string); + } return number; } }, diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a1d03f7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,167 @@ +{ + "name": "json-bigint", + "version": "0.2.3", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "assertion-error": { + "version": "1.0.0", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/assertion-error/-/assertion-error-1.0.0.tgz", + "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=", + "dev": true + }, + "bignumber.js": { + "version": "4.1.0", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==" + }, + "chai": { + "version": "1.9.2", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/chai/-/chai-1.9.2.tgz", + "integrity": "sha1-Pxog+CsLnXQ3V30k1vErGmnTtZA=", + "dev": true, + "requires": { + "assertion-error": "1.0.0", + "deep-eql": "0.1.3" + } + }, + "commander": { + "version": "2.0.0", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/commander/-/commander-2.0.0.tgz", + "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "0.1.1" + } + }, + "diff": { + "version": "1.0.7", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/diff/-/diff-1.0.7.tgz", + "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=", + "dev": true + }, + "glob": { + "version": "3.2.3", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "dev": true, + "requires": { + "graceful-fs": "2.0.3", + "inherits": "2.0.3", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "2.0.3", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", + "dev": true + }, + "growl": { + "version": "1.7.0", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/growl/-/growl-1.7.0.tgz", + "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "jade": { + "version": "0.26.3", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "dev": true, + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", + "dev": true + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "dev": true + } + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + }, + "mkdirp": { + "version": "0.3.5", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", + "dev": true + }, + "mocha": { + "version": "1.20.1", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/mocha/-/mocha-1.20.1.tgz", + "integrity": "sha1-80ODLZ/gx9l8ZPxwRI9RNt+f7Vs=", + "dev": true, + "requires": { + "commander": "2.0.0", + "debug": "3.1.0", + "diff": "1.0.7", + "glob": "3.2.3", + "growl": "1.7.0", + "jade": "0.26.3", + "mkdirp": "0.3.5" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "type-detect": { + "version": "0.1.1", + "resolved": "http://nexus3.dgcsdev.com/repository/npm/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + } + } +} diff --git a/test/bigint-test.js b/test/bigint-test.js index 5f5e1a3..78b92e4 100644 --- a/test/bigint-test.js +++ b/test/bigint-test.js @@ -28,4 +28,21 @@ describe("Testing bigint support", function(){ expect(output).to.equal(input); done(); }); + + [ + ['1.23456789012345678901', '1.23456789012345678901'], + ['12345678901234.5678901', '12345678901234.5678901'], + ['00000000000000.0000000', '0'] + ].forEach(function (data) { + var input = data[0]; + var expected = data[1]; + it('should convert any number longer than 15 chars to bignumber, even floats like' + input, function(done){ + var JSONbig = require('../index'); + var result = JSONbig.parse(input); + expect(result).to.be.instanceof(BigNumber); + expect(result.toString()).to.equal(expected); + done(); + }); + }) + }); diff --git a/test/float-hints-test.js b/test/float-hints-test.js new file mode 100644 index 0000000..c1ffb85 --- /dev/null +++ b/test/float-hints-test.js @@ -0,0 +1,33 @@ +var mocha = require('mocha') + , assert = require('chai').assert + , expect = require('chai').expect + , BigNumber = require('bignumber.js') +; + +describe("Testing 'strictFloatHints' option", function() { + + + it('should leave numbers with non-zero mantissa as floats', function(done) { + var JSONbig = require('../index')({floatHints: true}); + var obj = JSONbig.parse('12345678912345.123'); + expect(typeof obj).to.equal('number'); + done(); + }); + + it('should convert numbers with a zero mantissa to ints if the integer part is longer than 15 chars', function(done) { + var inputLong = '9223372036854775807.00000'; + var inputShort = '75807.00000'; + var JSONbig = require('../index')({floatHints: true}); + + var objLong = JSONbig.parse(inputLong); + expect(objLong, "instanceof big int").to.be.instanceof(BigNumber); + expect(objLong.toString(), "string from big int").to.equal("9223372036854775807"); + + var objShort = JSONbig.parse(inputShort); + expect(objShort).to.be.a('number'); + expect(objShort).to.equal(75807); + + done(); + }); + +}); diff --git a/test/strict-float-hints-test.js b/test/strict-float-hints-test.js new file mode 100644 index 0000000..c4c0189 --- /dev/null +++ b/test/strict-float-hints-test.js @@ -0,0 +1,49 @@ +var mocha = require('mocha') + , assert = require('chai').assert + , expect = require('chai').expect + , BigNumber = require('bignumber.js') +; + +describe("Testing 'strictFloatHints' option", function() { + + [ + '123.123', + '12345678901234567890.000', + '12345678901234567890.123', + '00000000000000000000.000' + ].forEach(function(input) { + it("Should treat input " + input + " as a float", function(done) { + var JSONbig = require('../index')({"strictFloatHints": true}); + var result = JSONbig.parse(input); + expect(result).to.be.a('number'); + done(); + }); + }); + + [ + '12345678901234567890', + '12345678901234567890', + '00000000000000000000' + ].forEach(function(input) { + it("Should treat input " + input + " as a BigNumber", function(done) { + var JSONbig = require('../index')({"strictFloatHints": true}); + var result = JSONbig.parse(input); + expect(result).to.be.instanceof(BigNumber); + done(); + }); + }); + + + [ + '123', + '0' + ].forEach(function(input) { + it("Should treat input " + input + " as an ordinary number", function(done) { + var JSONbig = require('../index')({"strictFloatHints": true}); + var result = JSONbig.parse(input); + expect(result).to.be.a('number'); + done(); + }); + }); + +});