diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 4f3182c0c1e7f11..b70e665caede23e 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -15,6 +15,25 @@ import json import sys +def parse_json_from_filehandle(infile, sort_keys): + try: + if sort_keys: + return json.load(infile) + else: + return json.load(infile, + object_pairs_hook=collections.OrderedDict) + except ValueError as e: + raise SystemExit(e) + +def parse_json_from_string(input, sort_keys): + try: + if sort_keys: + return json.loads(input) + else: + return json.loads(input, + object_pairs_hook=collections.OrderedDict) + except ValueError as e: + raise SystemExit(e) def main(): prog = 'python -m json.tool' @@ -27,23 +46,28 @@ def main(): help='write the output of infile to outfile') parser.add_argument('--sort-keys', action='store_true', default=False, help='sort the output of dictionaries alphabetically by key') + parser.add_argument('--jsonlines', action='store_true', default=False, + help='parse input using the jsonlines format') options = parser.parse_args() infile = options.infile or sys.stdin outfile = options.outfile or sys.stdout sort_keys = options.sort_keys - with infile: - try: - if sort_keys: - obj = json.load(infile) - else: - obj = json.load(infile, - object_pairs_hook=collections.OrderedDict) - except ValueError as e: - raise SystemExit(e) + jsonlines = options.jsonlines + objs = [] + + if jsonlines: + with infile: + for line in infile: + objs.append(parse_json_from_string(line, sort_keys)) + else: + with infile: + objs = [parse_json_from_filehandle(infile, sort_keys)] + with outfile: - json.dump(obj, outfile, sort_keys=sort_keys, indent=4) - outfile.write('\n') + for obj in objs: + json.dump(obj, outfile, sort_keys=sort_keys, indent=4) + outfile.write('\n') if __name__ == '__main__': diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 9d93f931ca39588..9729dd413d9efab 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -60,6 +60,27 @@ class TestTool(unittest.TestCase): ] """) + jsonlines_data = """\ + {"ingredients":["frog", "water", "chocolate", "glucose"]} + {"ingredients":["chocolate","steel bolts"]}\ + """ + + jsonlines_expect = textwrap.dedent("""\ + { + "ingredients": [ + "frog", + "water", + "chocolate", + "glucose" + ] + } + { + "ingredients": [ + "chocolate", + "steel bolts" + ] + }""") + def test_stdin_stdout(self): args = sys.executable, '-m', 'json.tool' with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc: @@ -92,6 +113,13 @@ def test_infile_outfile(self): self.assertEqual(out, b'') self.assertEqual(err, b'') + def test_jsonlines(self): + args = sys.executable, '-m', 'json.tool', '--jsonlines' + with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc: + out, err = proc.communicate(self.jsonlines_data.encode()) + self.assertEqual(out.splitlines(), self.jsonlines_expect.encode().splitlines()) + self.assertEqual(err, b'') + def test_help_flag(self): rc, out, err = assert_python_ok('-m', 'json.tool', '-h') self.assertEqual(rc, 0) diff --git a/Misc/NEWS.d/next/Library/2017-10-01-09-15-12.bpo-31553.0EubUh.rst b/Misc/NEWS.d/next/Library/2017-10-01-09-15-12.bpo-31553.0EubUh.rst new file mode 100644 index 000000000000000..98a20b2c5c8b7c7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-10-01-09-15-12.bpo-31553.0EubUh.rst @@ -0,0 +1,2 @@ +Add the --jsonlines option to json.tool in order to be able to parse input +sent using the jsonlines format.