Skip to content
shellhacks.dev
Python

Read a JSON file

Read a JSON file with json.load(f). A file saved with a byte order mark fails until opened as utf-8-sig, and a repeated key keeps only the last value.

2 min read
Summarize in AI

A JSON file that looks fine in an editor fails to parse with an error about a byte order mark.

Python
import json

with open("bom.json", "wb") as f:
    f.write(b'\xef\xbb\xbf{"a": 1}')
try:
    with open("bom.json", encoding="utf-8") as f:
        json.load(f)
except json.JSONDecodeError as e:
    print("error:", e)
with open("bom.json", encoding="utf-8-sig") as f:
    print(json.load(f))

with open("odd.json", "w") as f:
    f.write('{"a": 1, "a": 2, "n": NaN, "big": 12345678901234567890123}')
with open("odd.json") as f:
    print(json.load(f))
Text
error: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
{'a': 1}
{'a': 2, 'n': nan, 'big': 12345678901234567890123}

json.load reads a file object and json.loads a string. A file saved with a leading byte order mark, as some Windows tools do, is rejected by the utf-8 codec with a message that names the fix, and the utf-8-sig codec drops the mark. The second file shows behavior that surprises people: a repeated key silently keeps the last value, NaN is accepted although it is not JSON, and integers have no size limit. These are documented in the json module.

To reject the extensions, pass parse_constant to raise on NaN, and object_pairs_hook to catch duplicate keys. Use json.load on a file opened in text mode, or hand it bytes through json.loads, which detects the encoding. For very large files, a streaming parser such as ijson avoids loading everything at once.

Share

More snippets

Python

Base64 encode a string

Encode with base64.b64encode(), which takes and returns bytes, so encode the string first and decode the result. The URL-safe variant swaps two characters.

1 min read
Python

Convert a timestamp

Convert a Unix timestamp with datetime.fromtimestamp(ts, tz=timezone.utc). Without tz it uses the machine's zone, and milliseconds need a division by 1000.

1 min read
Python

Generate a UUID

Generate a random UUID with uuid.uuid4() and a repeatable one with uuid.uuid5(namespace, name). The hex attribute gives the form without hyphens.

2 min read

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.