jju
- a set of utilities to work with JSON / JSON5 documents
Installation
npm install jju
Usage
This module provides following functions:
- jju.parse() parses json/json5 text and returns a javascript value it corresponds to
- jju.stringify() converts javascript value to an appropriate json/json5 text
- jju.tokenize() parses json/json5 text and returns an array of tokens it consists of (see demo)
- jju.analyze() parses json/json5 text and tries to guess indentation, quoting style, etc.
- jju.update() changes json/json5 text, preserving original formatting as much as possible (see demo)
All functions are able to work with a standard JSON documents. jju.parse()
and jju.stringify()
are better in some cases, but slower than native JSON.parse()
and JSON.stringify()
versions. Detailed description see below.
jju.parse() function
jju.parse(text[, options])
jju.parse(text[, reviver])
Options:
- reserved_keys - what to do with reserved keys (String, default="ignore")
-
"ignore" - ignore reserved keys
-
"throw" - throw SyntaxError in case of reserved keys
-
"replace" - replace reserved keys, this is the default JSON.parse behaviour, unsafe
Reserved keys are keys that exist in an empty object (hasOwnProperty
, __proto__
, etc.).
parse('{hasOwnProperty: 1}', {reserved_keys: 'ignore'}) == {}
parse('{hasOwnProperty: 1, x: 2}', {reserved_keys: 'ignore'}).hasOwnProperty('x') == true
parse('{hasOwnProperty: 1}', {reserved_keys: 'throw'}) == SyntaxError
parse('{hasOwnProperty: 1}', {reserved_keys: 'throw'}) == {hasOwnProperty: 1}
parse('{hasOwnProperty: 1, x: 2}', {reserved_keys: 'ignore'}).hasOwnProperty('x') == TypeError
-
null_prototype - create object as Object.create(null) instead of '{}' (Boolean)
if reserved_keys != 'replace'
, default is false
if reserved_keys == 'replace'
, default is true
It is usually unsafe and not recommended to change this option to false in the last case.
-
reviver - reviver function - Function
This function should follow JSON specification
-
mode - operation mode, set it to 'json' if you want to throw on non-strict json files (String)
jju.stringify() function
jju.stringify(value[, options])
jju.stringify(value[, replacer [, indent])
Options:
-
ascii - output ascii only (Boolean, default=false)
If this option is enabled, output will not have any characters except of 0x20-0x7f.
-
indent - indentation (String, Number or Boolean, default='\t')
This option follows JSON specification.
-
quote - enquoting char (String, "'" or '"', default="'")
-
quote_keys - whether keys quoting in objects is required or not (String, default=false)
If you want {"q": 1}
instead of {q: 1}
, set it to true.
-
sort_keys - sort all keys while stringifying (Boolean or Function, default=false)
By default sort order will depend on implementation, with v8 it's insertion order. If set to true
, all keys (but not arrays) will be sorted alphabetically. You can provide your own sorting function as well.
-
replacer - replacer function or array (Function or Array)
This option follows JSON specification.
-
no_trailing_comma = don't output trailing comma (Boolean, default=false)
If this option is set, arrays like this [1,2,3,]
will never be generated. Otherwise they may be generated for pretty printing.
-
mode - operation mode, set it to 'json' if you want correct json in the output (String)
Currently it's either 'json' or something else. If it is 'json', following options are implied:
- options.quote = '"'
- options.no_trailing_comma = true
- options.quote_keys = true
- '\x' literals are not used
jju.tokenize() function
jju.tokenize(text[, options])
Options are the same as for the jju.parse
function.
Return value is an array of tokens, where each token is an object:
- raw (String) - raw text of this token, if you join all raw's, you will get the original document
- type (String) - type of the token, can be
whitespace
, comment
, key
, literal
, separator
or newline
- stack (Array) - path to the current token in the syntax tree
- value - value of the token if token is a
key
or literal
You can check tokenizer for yourself using this demo.
jju.analyze() function
jju.analyze(text[, options])
Options are the same as for the jju.parse
function.
Return value is an object defining a programming style in which the document was written.
- indent (String) - preferred indentation
- newline (String) - preferred newline
- quote (String) -
"
or '
depending on which quote is preferred - quote_keys (Boolean) -
true
if unquoted keys were used at least once - has_whitespace (Boolean) -
true
if input has a whitespace token - has_comments (Boolean) -
true
if input has a comment token - has_newlines (Boolean) -
true
if input has a newline token - has_trailing_comma (Boolean) -
true
if input has at least one trailing comma
jju.update() function
jju.update(text, new_value[, options])
If you want to update a JSON document, here is the general approach:
var input = '{"foo": "bar", "baz": 123}'
var json = jju.parse(input, {mode: 'json'})
json.foo = 'quux'
json.hello = 'world'
var output = jju.update(input, json, {mode: 'json'})
Look at this demo to test various types of json.
Advantages over existing JSON libraries
In a few cases it makes sense to use this module instead of built-in JSON methods.
Parser:
- better error reporting with source code and line numbers
In case of syntax error, JSON.parse does not return any good information to the user. This module does:
$ node -e 'require("jju").parse("[1,1,1,1,invalid]")'
SyntaxError: Unexpected token 'i' at 0:9
[1,1,1,1,invalid]
^
This module is about 5 times slower, so if user experience matters to you more than performance, use this module. If you're working with a lot of machine-generated data, use JSON.parse instead.
Stringifier:
- util.inspect-like pretty printing
This module behaves more smart when dealing with object and arrays, and does not always print newlines in them:
$ node -e 'console.log(require("./").stringify([[,,,],,,[,,,,]], {mode:"json"}))'
[
[null, null, null],
null,
null,
[null, null, null, null]
]
JSON.stringify will split this into 15 lines, and it's hard to read.
Yet again, this feature comes with a performance hit, so if user experience matters to you more than performance, use this module. If your JSON will be consumed by machines, use JSON.stringify instead.
As a rule of thumb, if you use "space" argument to indent your JSON, you'd better use this module instead.