10

I have a file that may or may not be empty. The goal is to read from the file an json object, append to it, and then write it back to the file. However, on the case that the file is empty, then json.load() fails. How do I resolve this without having to check if the file is empty beforehand?

Here is my current code:

with open(filename, 'a+') as infile:
    old_data = json.load(infile)
data = old_data + obj
with open(filename, 'w') as outfile:
    json.dump(data, outfile)

Error Message is:

 File "<stdin>", line 3, in <module>
  File "/usr/lib/python3.6/json/__init__.py", line 299, in load
    parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
  File "/usr/lib/python3.6/json/__init__.py", line 354, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.6/json/decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.6/json/decoder.py", line 357, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
PoorProgrammer
  • 459
  • 1
  • 4
  • 14
  • 3
    [Ask for forgiveness instead of permission.](https://stackoverflow.com/questions/12265451/ask-forgiveness-not-permission-explain) (use `try`/`except`). – pault Jul 24 '19 at 21:53
  • [This discussion](https://stackoverflow.com/questions/2507808/how-to-check-whether-a-file-is-empty-or-not) might help you – Alexandre B. Jul 24 '19 at 21:53
  • 1
    BTW, generally, this kind of situation is better handling by formatting your file as JSONL, not JSON. That way you can just append an extra line for the new data item and don't need to modify the content that's already there. See http://jsonlines.org/ – Charles Duffy Jul 24 '19 at 22:06
  • ...the current code, even after you fix the exception, will leave you with a corrupt file if it crashes (or your system loses power) partway through operation, or if a reader tries to consume the file while the write process just started. It's possible to fix that -- by writing to a temporary file, making sure it's fully sync'd to disk, and renaming it over the original, f/e -- but painful. Better to just use a format that lets you do an atomic append. – Charles Duffy Jul 24 '19 at 22:07
  • (Also, whereas an empty file isn't valid JSON, it's perfectly valid JSONL, so no exception handling is needed). – Charles Duffy Jul 24 '19 at 22:09

2 Answers2

9

To expand on @pault comment, you could use try/except, but it would work better in a better indented blocks (optionally, you can also chain the with statement):

from json.decoder import JSONDecodeError

with open(filename, 'a+') as infile, open(filename, 'w') as outfile:
    try:
        old_data = json.load(infile)
        data = old_data + obj
        json.dump(data, outfile)
    except JSONDecodeError:
        pass
Sazzy
  • 1,924
  • 3
  • 19
  • 27
  • 3
    The problem with this is that JSONDecodeError can be thrown for any random reason beside an empty file. – Pithikos Jan 06 '22 at 14:12
7
import os
os.stat("file").st_size == 0
  • I can imagine this could be useful if you would like to still have the program throw the JSONDecodeError for anything that isn't an empty file. As the main answer would also catch all the other decode errors. However, this answer would probably not account for files with a single space or other white space. – AgentM Mar 28 '20 at 19:17
  • also you could do file.read() and check the string – Hayden Thring Mar 10 '22 at 00:04