Skip to content
Closed
46 changes: 35 additions & 11 deletions Lib/json/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,25 @@
import json
import sys

def parse_json_from_filehandle(infile, sort_keys):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions are a bit repetitive and add a lot of unnecessary lines of code. I think they could be avoided by keeping the functionality in the original location. The code could become more like:

sort_keys = options.sort_keys
jsonlines = options.jsonlines
with infile:
    if jsonlines:
        if sort_keys:
            objs = [json.loads(line) for line in infile]
        else:
           ...
    else:
        if sort_keys:
            objs = [json.load(infile)]
        else:
            ...

This keeps the code more concise.

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'
Expand All @@ -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__':
Expand Down
28 changes: 28 additions & 0 deletions Lib/test/test_json/test_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add the --jsonlines option to json.tool in order to be able to parse input
sent using the jsonlines format.