diff --git a/README.md b/README.md index 906b0fa..d6221cf 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,14 @@ comment that can span lines /* {"foo": "bar", "bacon": "eggs"} ``` +You can also use `json5.tool` for validating json. + +``` +python3 -m json5.tool < myfile.json5 +``` +As of right now, there is no formatting on the output; original whitespace and comments is maintained. +In the future, pretty-printing, stripping comments, and other options may be added. + See also the [full documentation](https://json-five.readthedocs.io/en/latest/) ## Key features diff --git a/json5/dumper.py b/json5/dumper.py index ee2b340..1915477 100644 --- a/json5/dumper.py +++ b/json5/dumper.py @@ -178,7 +178,7 @@ def process_leading_wsc(self, node): @singledispatchmethod def dump(self, node): - raise NotImplementedError('foo') + raise NotImplementedError(f'Cannot dump node of type {type(node)}') to_json = dump.register diff --git a/json5/loader.py b/json5/loader.py index 9dd0bde..5de11c1 100644 --- a/json5/loader.py +++ b/json5/loader.py @@ -30,7 +30,7 @@ def load(f, **kwargs): :return: """ text = f.read() - return loads(text) + return loads(text, **kwargs) def loads(s, *args, loader=None, **kwargs): """ diff --git a/json5/tool.py b/json5/tool.py new file mode 100644 index 0000000..b2e72a4 --- /dev/null +++ b/json5/tool.py @@ -0,0 +1,46 @@ +import argparse +from json5 import loads, dump, load +from json5.loader import ModelLoader +from json5.dumper import ModelDumper +import sys + + +def main(): + prog = 'python -m json5.tool' + description = ('Command line interface for the json5 module ' + 'analogous to the json.tool CLI.') + parser = argparse.ArgumentParser(prog=prog, description=description) + parser.add_argument('infile', nargs='?', + type=argparse.FileType(encoding="utf-8"), + help='a JSON file to be validated', + default=sys.stdin) + parser.add_argument('outfile', nargs='?', + type=argparse.FileType('w', encoding="utf-8"), + help='write the output of infile to outfile', + default=sys.stdout) + parser.add_argument('--json-lines', action='store_true', default=False, + help='parse input using the JSON Lines format.') + options = parser.parse_args() + + dump_args = { + 'dumper': ModelDumper() + } + + with options.infile as infile, options.outfile as outfile: + try: + if options.json_lines: + objs = (loads(line, loader=ModelLoader()) for line in infile) + else: + objs = (load(infile, loader=ModelLoader()), ) + for obj in objs: + dump(obj, outfile, **dump_args) + outfile.write('\n') + except ValueError as e: + raise SystemExit(e) + + +if __name__ == '__main__': + try: + main() + except BrokenPipeError as exc: + sys.exit(exc.errno) \ No newline at end of file diff --git a/setup.py b/setup.py index e873b84..37379a6 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='json-five', - version='0.6.5', + version='0.7.5', packages=['json5'], url='https://github.com/spyoungtech/json-five', license='Apache', diff --git a/tests/test_json_tool.py b/tests/test_json_tool.py new file mode 100644 index 0000000..17ec3d1 --- /dev/null +++ b/tests/test_json_tool.py @@ -0,0 +1,35 @@ +import subprocess +import pytest +import io +@pytest.mark.parametrize('json_string', [ +"""{"foo":"bar"}""", +"""{"foo": "bar"}""", +"""{"foo":"bar","bacon":"eggs"}""", +"""{"foo": "bar", "bacon" : "eggs"}""", +"""["foo","bar","baz"]""", +"""[ "foo", "bar" , "baz" ]""", +"""{"foo":\n "bar"\n}""", +"""{"foo": {"bacon": "eggs"}}""", +""" {"foo":"bar"}""", +"""{"foo": "bar"} """, +"""{'foo': 'bar'}""", +"""{"foo": 'bar'}""", +"""{"foo": "bar",}""", +"""["foo","bar", "baz",]""", +"""["foo", "bar", "baz", ]""", +"""["foo", "bar", "baz" ,]""", +"""[["foo"], ["foo","bar"], "baz"]""", +"""{unquoted: "foo"}""", +"""{unquoted: "foo"}""", +"""["foo"]""", +"""["foo" , ]""", +]) +def test_tool(json_string): + r = subprocess.run( + ["python3", "-m", "json5.tool"], + universal_newlines=True, + input=json_string, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + assert r.stdout[:-1] == json_string