d3x0r / JSON6

JSON for Humans (ES6)
Other
237 stars 15 forks source link
javascript json json-parsing

JSON6 – JSON for Humans

Build Status

Join the chat at https://gitter.im/sack-vfs/json6

Documentation base cloned from JSON5 project https://github.com/json5/json5

JSON is an excellent data format, but can be better, and more expressive.

JSON6 is a proposed extension to JSON (Proposed here, noone, like em-discuss seemed to care about such a thing; prefering cryptic solutions like json-schema, or the 1000 pound gorilla solution). It aims to make it easier for humans to write and maintain by hand. It does this by adding some minimal syntax features directly from ECMAScript 6.

JSON6 is a superset of JavaScript, although adds no new data types, and works with all existing JSON content. Some features allowed in JSON6 are not directly supported by Javascript; although all javascript parsable features can be used in JSON6, except functions or any other code construct, transporting only data save as JSON.

JSON6 is not an official successor to JSON, and JSON6 content may not work with existing JSON parsers. For this reason, JSON6 files use a new .json6 extension. (TODO: new MIME type needed too.)

The code is a reference JavaScript implementation for both Node.js and all browsers. It is a completly new implementation.

Other related : JSOX JS Object Exchange format, which builds upon this and adds additional support for Date, BigNum, custom emissions, keyword-less class defintitions;default initializers, data condensation, flexible user exensibility.

Why

JSON isn’t the friendliest to write. Keys need to be quoted, objects and arrays can’t have trailing commas, and comments aren’t allowed — even though none of these are the case with regular JavaScript today.

That was fine when JSON’s goal was to be a great data format, but JSON’s usage has expanded beyond machines. JSON is now used for writing configs, manifests, even tests — all by humans.

There are other formats that are human-friendlier, like YAML, but changing from JSON to a completely different format is undesirable in many cases. JSON6’s aim is to remain close to JSON and JavaScript.

Features

The following is the exact list of additions to JSON’s syntax introduced by JSON6. All of these are optional, and MOST of these come from ES5/6.

Caveats

Does not include stringify, instead falling back to original (internal) JSON.stringify. This will cause problems maintaining undefined, Infinity and NaN type values.

Summary of Changes from JSON5

JSON6 includes all features of JSON5 plus the following.

Objects

Arrays

Strings

Numbers

Keyword Values

Comments

Example

The following is a contrived example, but it illustrates most of the features:

{
    foo: 'bar',
    while: true,
    nothing : undefined, // why not?

    this: 'is a \
multi-line string',

    thisAlso: 'is a
multi-line string; but keeps newline',

    // this is an inline comment
    here: 'is another', // inline comment

    /* this is a block comment
       that continues on another line */

    hex: 0xDEAD_beef,
    binary: 0b0110_1001,
    decimal: 123_456_789,
    octal: 0o123,
    half: .5,
    delta: +10,
    negative : ---123,
    to: Infinity,   // and beyond!

    finally: 'a trailing comma',
    oh: [
        "we shouldn't forget",
        'arrays can have',
        'trailing commas too',
    ],
}

This implementation’s own package.JSON6 is more realistic:

// This file is written in JSON6 syntax, naturally, but npm needs a regular
// JSON file, so compile via `npm run build`. Be sure to keep both in sync!

{
    name: 'JSON6',
    version: '0.1.105',
    description: 'JSON for the ES6 era.',
    keywords: ['json', 'es6'],
    author: 'd3x0r <d3x0r@github.com>',
    contributors: [
        // TODO: Should we remove this section in favor of GitHub's list?
        // https://github.com/d3x0r/JSON6/contributors
    ],
    main: 'lib/JSON6.js',
    bin: 'lib/cli.js',
    files: ["lib/"],
    dependencies: {},
    devDependencies: {
        gulp: "^3.9.1",
        'gulp-jshint': "^2.0.0",
        jshint: "^2.9.1",
        'jshint-stylish': "^2.1.0",
        mocha: "^2.4.5"
    },
    scripts: {
        build: 'node ./lib/cli.js -c package.JSON6',
        test: 'mocha --ui exports --reporter spec',
            // TODO: Would it be better to define these in a mocha.opts file?
    },
    homepage: 'http://github.com/d3x0r/JSON6/',
    license: 'MIT',
    repository: {
        type: 'git',
        url: 'https://github.com/d3x0r/JSON6',
    },
}

Community

Join the Google Group if you’re interested in JSON6 news, updates, and general discussion. Don’t worry, it’s very low-traffic.

The GitHub wiki (will be) a good place to track JSON6 support and usage. Contribute freely there!

GitHub Issues is the place to formally propose feature requests and report bugs. Questions and general feedback are better directed at the Google Group.

Usage

This JavaScript implementation of JSON6 simply provides a JSON6 object just like the native ES5 JSON object.

To use from Node:

npm install json-6
var JSON6 = require('json-6');

To use in the browser (adds the JSON6 object to the global namespace):

<script src="https://github.com/d3x0r/JSON6/raw/master/node_modules/json-6/lib/json6.js"></script>

Then in both cases, you can simply replace native JSON calls with JSON6:

var obj = JSON6.parse('{unquoted:"key",trailing:"comma",}');
var str = JSON6.stringify(obj); /* uses JSON stringify, so don't have to replace */
JSON6 Methods parameters Description
parse (string [,reviver]) supports all of the JSON6 features listed above, as well as the native reviver argument.
stringify ( value ) converts object to JSON. stringify
escape ( string ) substitutes ", \, ', and ` with backslashed sequences. (prevent 'JSON injection')
begin (cb [,reviver] ) create a JSON6 stream processor. cb is called with (value) for each value decoded from input given with write(). Optional reviver is called with each object before being passed to callback.

JSON6 Streaming

A Parser that returns objects as they are encountered in a stream can be created. JSON.begin( dataCallback, reviver ); The callback is called for each complete object in a stream of data that is passed.

JSON6.begin( cb, reviver ) returns an object with a few methods.

Method Arguments Description
write (string) Parse string passed and as objects are found, invoke the callback passed to begin() Objects are passed through optional reviver function passed to begin().
_write (string,completeAtEnd) Low level routine used internally. This does the work of parsing the passed string. Returns 0 if no object completed, 1 if there is no more data, and an object was completd, returns 2 if there is more data and a parsed object is found. if completedAtEnd is true, dangling values are returned, for example "1234" isn't known to be completed, more of the number might follow in another buffer; if completeAtEnd is passed, this iwll return as number 1234. Passing empty arguments steps to the next buffered input value.
value () Returns the currently completed object. Used to get the completed object after calling _write.
reset () If write() or \_write() throws an exception, no further objects will be parsed becuase internal status is false, this resets the internal status to allow continuing using the existing parser. ( May require some work to actually work for complex cases)
    // This is (basically) the internal loop that write() uses.
    var result
    for( result = this._write(msg,false); result > 0; result = this._write() ) {
        var obj = this.value();
        // call reviver with (obj)
        // call callback with (obj)
    }
// Example code using write
function dataCallback( value ) {
    console.log( "Value from stream:", value );
}
var parser = JSON.begin( dataCallback );

parser.write( '"Hello ' );   // a broken simple value string, results as 'Hello World!'
parser.write( 'World!"' );
parser.write( '{ first: 1,' );   // a broken structure
parser.write( ' second : 2 }' );
parser.write( '[1234,12');  // a broken array across a value
parser.write( '34,1234]');
parser.write( '1234 456 789 123 523');  // multiple single simple values that are numbers
parser.write( '{a:1} {b:2} {c:3}');  // multiple objects

parser.write( '1234' );  // this won't return immediately, there might be more numeric data.
parser.write( '' ); // flush any pending numbers; if an object or array or string was split, throws an error; missing close.

parser.write( '1234' );
parser.write( '5678 ' );  // at this point, the space will flush the number value '12345678'

Extras

If you’re running this on Node, you can also register a JSON6 require() hook to let you require() .json6 files just like you can .json files:

require('JSON-6/lib/require');
require('./path/to/foo');   // tries foo.json6 after foo.js, foo.json, etc.
require('./path/to/bar.json6');

This module also provides a json6 executable (requires Node) for converting JSON6 files to JSON:

json6 -c path/to/foo.json6  # generates path/to/foo.json

Other Implementations

This is also implemented as part of npm [sack.vfs https://www.npmjs.com/package/sack.vfs] as a native code node.js addon. This native javascript version allows usage in browsers.

Benchmarks

This is as fast as the javascript version of Douglas Crockford's reference implementation JSON implementation for JSON parsing.

This is nearly double the speed of [JSON5 http://json5.org] implementation that inspired this (which is half the speed of Crockford's reference implementation).

This is half the speed of the sack.vfs native C++ node addon implementation (which itself is half the speed of V8's native code implementation, but they can cheat and build strings directly).

Requirements

Currently engines is set for Node 10 or higher.

However, let, const, and new unicode string support for codepoints (like codePointAt), are the most exotic of features used by the library.

Tests may include arrow functions.

For development purposes, this is tooled to always use the latest build tools, which require a minimum platform of their own.

External development dependencies

Development

git clone https://github.com/d3x0r/json6
cd json6
npm install
npm test

As the package.json6 file states, be sure to run npm run build on changes to package.json6, since npm requires package.json.

Feel free to file issues and submit pull requests — contributions are welcome. If you do submit a pull request, please be sure to add or update the tests, and ensure that npm test continues to pass.

Continuous Integration Testing

Travis CI is used to automatically test the package when pushed to github. Recently .mjs tests have been added, and rather than 1) build a switch to test mocha/test/*.js instead of just *, and 2) depending on node version switch the test command which is run, the older platforms were removed from testing.

The product of this should run on very old platforms also, especially node_modules/json-6/dist/index.min.js.

Changelog

License

MIT. See LICENSE.md for details.

Credits

(http://github.com/json5/json5) Inspring this project.