Python's standard library includes the json module, which converts between JSON text and Python objects. It covers almost everything you need, but a few defaults surprise people: non-ASCII characters are escaped, dates aren't supported, and floats lose decimal precision. This guide covers reading and writing JSON, custom types, and the common errors.
Use json.loads for a string and json.load for a file:
pythonimport json data = json.loads('{"name": "Asha", "age": 30, "active": true}') data["name"] # 'Asha' data["active"] # True with open("config.json", encoding="utf-8") as f: config = json.load(f)
Always open files with encoding="utf-8". JSON is UTF-8 by default, but Python's default file encoding depends on the operating system.
| JSON | Python |
|---|---|
| object | dict |
| array | list |
| string | str |
| number (integer) | int |
| number (with decimals) | float |
| true / false | True / False |
| null | None |
Python's int has no size limit, so large integer IDs parse exactly, unlike in JavaScript.
pythonjson.dumps({"name": "Asha", "tags": ["admin"]}) # '{"name": "Asha", "tags": ["admin"]}' with open("out.json", "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False)
Useful options:
indent=2 pretty prints; see How to pretty print JSON.ensure_ascii=False writes café instead of café.sort_keys=True sorts object keys for stable output; see Sort JSON keys.separators=(",", ":") produces compact output with no spaces; see How to minify JSON.json.dumps raises TypeError: Object of type datetime is not JSON serializable for anything outside the table above. The common cases are datetime, Decimal, UUID, set and dataclasses. Handle them with a default function:
pythonimport json, datetime, decimal, uuid, dataclasses def to_json(value): if isinstance(value, (datetime.datetime, datetime.date)): return value.isoformat() if isinstance(value, decimal.Decimal): return str(value) # keep exact decimal value if isinstance(value, uuid.UUID): return str(value) if isinstance(value, set): return sorted(value) if dataclasses.is_dataclass(value): return dataclasses.asdict(value) raise TypeError(f"Cannot serialize {type(value).__name__}") json.dumps(record, default=to_json)
Raising TypeError for unknown types is important: silently converting everything with str() hides bugs.
By default, numbers with decimals become float, which can't represent many decimal values exactly. To keep prices exact, parse them as Decimal:
pythonfrom decimal import Decimal json.loads('{"price": 19.99}', parse_float=Decimal) # {'price': Decimal('19.99')}
print(my_dict) looks like JSON but isn't: it uses single quotes, True, and None. Pasting that output into a JSON tool fails. Use json.dumps(my_dict) whenever you need real JSON, for example in logs or test fixtures. If you already have Python-literal text, ast.literal_eval can read it back into a dict.
Invalid input raises json.JSONDecodeError, which tells you exactly where parsing failed:
pythontry: json.loads(text) except json.JSONDecodeError as e: print(e.msg, "at line", e.lineno, "column", e.colno)
The usual causes are trailing commas, single quotes, comments, and empty or HTML responses from an API; see Fix "Unexpected token" and other JSON parse errors.
json.load reads the whole file into memory. For very large arrays, stream items with a library such as ijson. For logs, prefer NDJSON / JSON Lines and parse one line at a time.
json.loads only checks syntax. To check that required fields and types are present, validate against a schema with the jsonschema package; see JSON Schema basics.