ujson
ujson, or UltraJSON, is a Python JSON encoder and decoder implemented in C. Its loads, dumps, load, and dump functions resemble the standard json module, with extra output switches for HTML characters and forward slashes. It remains fast and widely installed, and current wheels cover many Python versions and platforms. The decisive fact is in the project's own README: its architecture is considered risky to change safely, the project is maintenance-only, and its maintainers tell users to migrate to orjson.
Do not add ujson to a new project. Keep it only where measured performance and compatibility justify the migration delay, then plan the move its own maintainers recommend.
Use it if
- You maintain an existing application that already depends on ujson and changing JSON behavior now is riskier than keeping it pinned
- You have benchmarks from your own payloads showing ujson materially helps a hot path and orjson's bytes output would force disruptive changes
- You need a near-json-module string-returning API while completing a planned migration away from ujson
- You are choosing a JSON library for a new project: the maintainers explicitly place ujson in maintenance-only mode and recommend orjson because this C architecture risks future buffer-overflow vulnerabilities
- You assume the fastest-sounding package is fastest: ujson's own published benchmark table shows orjson substantially ahead on its tested encode and decode workloads
- You do not have benchmark evidence that Python's standard json is a bottleneck: an extra native dependency and behavioral compatibility testing are needless costs otherwise
- You deploy to a platform without a matching wheel: installation falls back to a native C build and needs a compiler toolchain, with additional ABI concerns if you link a system double-conversion library
Setup reality
python -m pip install ujson usually selects one of the project's many prebuilt wheels and type stubs are included. Python 3.10 or newer is required. Less common interpreters, architectures, or distribution policies may force a C build; system double-conversion builds add ABI compatibility duties. Treat a move from json as a data-format change: test Unicode, NaN, custom types, byte input, ordering, and exact output before swapping imports.
Patterns
Encode a Python valueencode-json
import ujson
payload = {'ok': True, 'items': [1, 2, 3]}
text = ujson.dumps(payload)
print(text)ujson.dumps returns str. This differs from orjson.dumps, which returns bytes.
Decode JSON text or bytesdecode-json
data = ujson.loads(b'{"name":"Ada","active":true}')
print(data['name'])loads accepts str, bytes, or bytearray, but malformed input raises ujson.JSONDecodeError.
Handle malformed inputhandle-decode-error
try:
data = ujson.loads(raw_body)
except ujson.JSONDecodeError as exc:
raise ValueError('invalid JSON payload') from excCatch the specific decode exception when bad client input is expected; it is also a ValueError subclass.
Write JSON to a text filewrite-json-file
with open('report.json', 'w', encoding='utf-8') as fp:
ujson.dump(report, fp, ensure_ascii=False, indent=2)dump writes text, so open the file in text mode with an explicit encoding.
Read JSON from a fileread-json-file
with open('report.json', 'r', encoding='utf-8') as fp:
report = ujson.load(fp)load expects a file-like object with read(); passing a path string does not open the file.
Keep non-ASCII characters readableemit-utf8-text
text = ujson.dumps(
{'city': 'Malmö'},
ensure_ascii=False,
escape_forward_slashes=False,
)ensure_ascii defaults to true and forward slashes default to escaped, so set both when compact UTF-8 text matters.
Reject NaN and Infinityreject-nonstandard-numbers
text = ujson.dumps(measurements, allow_nan=False)JSON does not define NaN or Infinity. The strict option prevents producing values that other parsers may reject.
Convert unsupported values explicitlyserialize-custom-type
from datetime import datetime
def json_default(value):
if isinstance(value, datetime):
return value.isoformat()
raise TypeError(f'unsupported type: {type(value).__name__}')
text = ujson.dumps(event, default=json_default)Let the default function raise for unknown types; silently stringifying everything can hide data-model mistakes.
Escape HTML-sensitive charactersescape-html-characters
text = ujson.dumps(
{'fragment': '<script>alert(1)</script>'},
encode_html_chars=True,
)This escapes angle brackets and ampersands, but JSON encoding alone is not a complete defense for every HTML or script context.
Sort keys and control separatorsproduce-stable-output
text = ujson.dumps(
record,
sort_keys=True,
separators=(',', ': '),
ensure_ascii=False,
)Sorted keys help snapshots and diffs, but do not turn JSON into a cryptographic canonicalization format.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| orjson | PyPI | You want the replacement recommended by ujson's maintainers and can accept dumps returning bytes |
| msgspec | PyPI | You want fast JSON plus typed schema validation and structured decoding |
| python-rapidjson | PyPI | You need RapidJSON-specific parse, number, datetime, or UUID controls |