Here you go: a tested solution based on the answer from @Brien
This should be able to handle any arbitrary sized input file. It is a generator, so it yields up dictionary objects one at a time as it parses them out of the JSON input file.
If you run it as a stand-alone, it runs three test cases. (In the if __name__ == "__main__"
block)
Of course, to make this read from standard input you would simply pass sys.stdin
as the input file argument.
import json
_DECODER = json.JSONDecoder()
_DEFAULT_CHUNK_SIZE = 4096
_MB = (1024 * 1024)
_LARGEST_JSON_OBJECT_ACCEPTED = 16 * _MB # default to 16 megabytes
def json_objects_from_file(input_file,
chunk_size=_DEFAULT_CHUNK_SIZE,
max_size=_LARGEST_JSON_OBJECT_ACCEPTED):
"""
Read an input file, and yield up each JSON object parsed from the file.
Allocates minimal memory so should be suitable for large input files.
"""
buf = ''
while True:
temp = input_file.read(chunk_size)
if not temp:
break
# Accumulate more input to the buffer.
#
# The decoder is confused by leading white space before an object.
# So, strip any leading white space if any.
buf = (buf + temp).lstrip()
while True:
try:
# Try to decode a JSON object.
x, i = _DECODER.raw_decode(buf)
# If we got back a dict, we got a whole JSON object. Yield it.
if type(x) == dict:
# First, chop out the JSON from the buffer.
# Also strip any leading white space if any.
buf = buf[i:].lstrip()
yield x
except ValueError:
# Either the input is garbage or we got a partial JSON object.
# If it's a partial, maybe appending more input will finish it,
# so catch the error and keep handling input lines.
# Note that if you feed in a huge file full of garbage, this will grow
# very large. Blow up before reading an excessive amount of data.
if len(buf) >= max_size:
raise ValueError("either bad input or too-large JSON object.")
break
buf = buf.strip()
if buf:
if len(buf) > 70:
buf = buf[:70] + '...'
raise ValueError('leftover stuff from input: "{}"'.format(buf))
if __name__ == "__main__":
from StringIO import StringIO
jstring = '{"menu":\n"a"}{"c": []\n}\n{\n"d": [3,\n 2]}{\n"e":\n "}"}'
f = StringIO(jstring)
correct = [{u'menu': u'a'}, {u'c': []}, {u'd': [3, 2]}, {u'e': u'}'}]
result = list(json_objects_from_file(f, chunk_size=3))
assert result == correct
f = StringIO(' ' * (17 * _MB))
correct = []
result = list(json_objects_from_file(f, chunk_size=_MB))
assert result == correct
f = StringIO('x' * (17 * _MB))
correct = "ok"
try:
result = list(json_objects_from_file(f, chunk_size=_MB))
except ValueError:
result = correct
assert result == correct