json.dumps
Python objects in, JSON text out. Tuples turn into arrays, every key turns into a string, and anything json does not know — sets, dates, Decimals — needs default=.
Demo
import json record = {'name': 'Ada', 'tags': ['admin', 'dev'], 'score': 9.5} json.dumps(record)
In "accented", ë is written as \u00eb; the emoji is above U+FFFF, so ensure_ascii writes it as a surrogate pair of two \u escapes. Floats are written with repr(), so 10 typed as a float comes out as 10.0 and 1e16 as 1e+16. With indent set, the item separator drops its trailing space, and indent=0 still breaks lines. In non-str keys, the key comes back from loads as the string '1', not the int 1.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| obj | any | yes | dict, list, tuple, str, int, float, bool or None — nested freely. Anything else goes through default. |
| indent | int | str | None | no (None) | None = one line. An int puts every item on its own line, indented by that many spaces per level (0 = new lines, no spaces); a str is used as the indent itself. |
| sort_keys | bool | no (False) | Write dict keys in sorted order instead of insertion order. Keys of mixed types (1 and "a") raise TypeError. |
| ensure_ascii | bool | no (True) | Escape every non-ASCII character as \uXXXX (a surrogate pair above U+FFFF). False writes the characters as they are. |
| separators | tuple[str, str] | no (None) | (item_separator, key_separator). Default (', ', ': '), or (',', ': ') when indent is set; (',', ':') gives the most compact output. |
| default | callable | no (None) | Called with every object json cannot serialize; must return something it can (or raise TypeError). |
| allow_nan | bool | no (True) | Write NaN, Infinity and -Infinity (not valid JSON). False raises ValueError instead. |
| skipkeys | bool | no (False) | Silently drop dict keys that are not str, int, float, bool or None instead of raising TypeError. |
| check_circular | bool | no (True) | Detect a container that contains itself (ValueError). False skips the check — a cycle then ends in RecursionError. |
| cls | JSONEncoder subclass | no (None) | Encoder class to use; the other keyword arguments are passed to it. |
Return value
str — The JSON text. Never bytes — call .encode() yourself if you need them.
Common patterns
import json print(json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False))
import json body = json.dumps(payload, separators=(',', ':'))
import json from datetime import date, datetime from decimal import Decimal from uuid import UUID def to_json(o): if isinstance(o, (date, datetime)): return o.isoformat() if isinstance(o, (Decimal, UUID)): return str(o) raise TypeError(f'Object of type {type(o).__name__} is not JSON serializable') json.dumps(record, default=to_json)
import hashlib, json key = hashlib.sha256(json.dumps(params, sort_keys=True, separators=(',', ':')).encode()).hexdigest()
Examples
Pitfalls
import json json.dumps({'tags': {'admin'}})
import json json.dumps({'tags': sorted({'dev', 'admin'})})
import json json.dumps(json.dumps({'a': 1}))
import json json.dumps({'a': 1})
import json json.dumps({1: 'a', 'b': 2}, sort_keys=True)
import json data = {1: 'a', 'b': 2} json.dumps({str(k): v for k, v in data.items()}, sort_keys=True)
import json json.dumps({'city': 'Zürich'})
import json json.dumps({'city': 'Zürich'}, ensure_ascii=False)
When to use
- You need JSON as a str — HTTP bodies, message queues, database columns, logs
- Pretty-printing data for a person (indent=2)
- A deterministic text form of a dict (sort_keys=True) for hashing or diffing
- Writing to a file → json.dump(data, f) streams into it
- Round-tripping Python types (tuples, sets, dates) exactly → pickle, or a schema library
- Serializing many custom classes → a JSONEncoder subclass keeps default() in one place
Notes
FAQ
json.dumps(data, indent=2) puts every item on its own line with two spaces per level. Add sort_keys=True for a stable key order and ensure_ascii=False to keep non-ASCII text readable.