orjson is the fastest Python JSON library — serializes dataclasses, datetimes, numpy arrays, and UUIDs natively. pip install orjson. Dump: import orjson; data = orjson.dumps({"key": "value"}) → bytes. Load: orjson.loads(data) → dict. String: orjson.dumps(obj).decode(). Options: orjson.dumps(obj, option=orjson.OPT_INDENT_2). OPT_SORT_KEYS: sort dict keys. OPT_NON_STR_KEYS: allow non-string keys. OPT_SERIALIZE_NUMPY: numpy arrays as JSON arrays. OPT_SERIALIZE_UUID: UUID to string. OPT_PASSTHROUGH_DATETIME: skip datetime → use default. OPT_UTC_Z: datetime UTC as “Z” suffix. OPT_NAIVE_UTC: naive datetime treated as UTC. OPT_STRICT_INTEGER: raise on int > 64-bit. OPT_APPEND_NEWLINE: append \n. Stack: option=OPT_A | OPT_B. Default: def default(obj): if isinstance(obj, Decimal): return str(obj). Django/Flask: return HttpResponse(orjson.dumps(data), content_type="application/json"). FastAPI: class OResponse(JSONResponse): def render(self, content): return orjson.dumps(content). Pydantic: orjson.dumps(model.model_dump()). Dataclass: @dataclass instances serialize field-by-field natively. numpy: import numpy as np; orjson.dumps(arr, option=orjson.OPT_SERIALIZE_NUMPY). UUID: auto-serialized to string. datetime: auto → ISO 8601. datetime.datetime datetime.date datetime.time. decimal.Decimal via default. Speed: 3–10× faster than stdlib json. orjson.dumps returns bytes (not str) — str() or .decode() if needed. Claude Code generates orjson serialization helpers, FastAPI response classes, and high-throughput data pipeline encoders.
CLAUDE.md for orjson
## orjson Stack
- Version: orjson >= 3.9 | pip install orjson
- Dump: orjson.dumps(obj) → bytes | orjson.dumps(obj).decode() → str
- Load: orjson.loads(data) → Python object (accepts bytes, str, memoryview)
- Options: OPT_INDENT_2 | OPT_SORT_KEYS | OPT_NON_STR_KEYS | OPT_SERIALIZE_NUMPY
- Native: datetime → ISO 8601; UUID → str; dataclass/attrs → fields; numpy → array
- Custom: orjson.dumps(obj, default=fn) — raise TypeError to reject unknown
- FastAPI: subclass JSONResponse, override render() to use orjson.dumps
orjson Fast JSON Pipeline
# app/json_utils.py — orjson fast serialization, custom types, and FastAPI helpers
from __future__ import annotations
import dataclasses
import datetime
import decimal
import io
import uuid
from pathlib import Path
from typing import Any, Callable
import orjson
# ─────────────────────────────────────────────────────────────────────────────
# 1. Core helpers
# ─────────────────────────────────────────────────────────────────────────────
def dumps(
obj: Any,
*,
indent: bool = False,
sort_keys: bool = False,
append_newline: bool = False,
default: Callable | None = None,
option: int | None = None,
) -> bytes:
"""
Serialize obj to JSON bytes using orjson.
Convenience wrapper with named keyword args for common options.
"""
opt = option or 0
if indent:
opt |= orjson.OPT_INDENT_2
if sort_keys:
opt |= orjson.OPT_SORT_KEYS
if append_newline:
opt |= orjson.OPT_APPEND_NEWLINE
kw: dict[str, Any] = {}
if opt:
kw["option"] = opt
if default:
kw["default"] = default
return orjson.dumps(obj, **kw)
def dumps_str(obj: Any, **kwargs) -> str:
"""Serialize to JSON string (decoded from bytes)."""
return dumps(obj, **kwargs).decode()
def loads(data: bytes | str | memoryview) -> Any:
"""Deserialize JSON bytes or string to Python object."""
return orjson.loads(data)
def round_trip(obj: Any, **kwargs) -> Any:
"""Pack then immediately unpack — useful for type normalization."""
return loads(dumps(obj, **kwargs))
# ─────────────────────────────────────────────────────────────────────────────
# 2. Custom type default encoder
# ─────────────────────────────────────────────────────────────────────────────
def default_encoder(obj: Any) -> Any:
"""
orjson default function supporting extra types.
Pass as: orjson.dumps(obj, default=default_encoder)
"""
if isinstance(obj, decimal.Decimal):
return str(obj)
if isinstance(obj, (set, frozenset)):
return sorted(obj, key=str)
if isinstance(obj, bytes):
return obj.hex()
if isinstance(obj, Path):
return str(obj)
if isinstance(obj, complex):
return {"real": obj.real, "imag": obj.imag}
if hasattr(obj, "__dict__"):
return obj.__dict__
raise TypeError(f"Object of type {type(obj).__name__!r} is not JSON serializable")
def dumps_extended(obj: Any, indent: bool = False, sort_keys: bool = False) -> bytes:
"""Serialize with extended type support (Decimal, set, bytes, Path, etc.)."""
return dumps(obj, indent=indent, sort_keys=sort_keys, default=default_encoder)
def dumps_extended_str(obj: Any, **kwargs) -> str:
return dumps_extended(obj, **kwargs).decode()
# ─────────────────────────────────────────────────────────────────────────────
# 3. Numpy / scientific data
# ─────────────────────────────────────────────────────────────────────────────
def dumps_numpy(obj: Any, indent: bool = False) -> bytes:
"""
Serialize object with numpy array support.
Requires: pip install numpy
Arrays become JSON arrays; dtype information is discarded.
"""
opt = orjson.OPT_SERIALIZE_NUMPY
if indent:
opt |= orjson.OPT_INDENT_2
return orjson.dumps(obj, option=opt)
def serialize_dataframe(df, orient: str = "records") -> bytes:
"""
Serialize a pandas DataFrame using orjson.
orient: "records" | "columns" (dict of lists)
"""
if orient == "records":
records = df.to_dict(orient="records")
else:
records = df.to_dict(orient="list")
return dumps(records, default=default_encoder)
# ─────────────────────────────────────────────────────────────────────────────
# 4. File helpers
# ─────────────────────────────────────────────────────────────────────────────
def load_json_file(path: str | Path) -> Any:
"""Fast-load a JSON file using orjson."""
return orjson.loads(Path(path).read_bytes())
def save_json_file(
obj: Any,
path: str | Path,
indent: bool = True,
sort_keys: bool = False,
default: Callable | None = None,
) -> None:
"""Save obj as a JSON file using orjson."""
data = dumps(obj, indent=indent, sort_keys=sort_keys, default=default or default_encoder)
Path(path).write_bytes(data)
def update_json_file(path: str | Path, update_fn: Callable[[Any], Any]) -> Any:
"""Read, transform, and write a JSON file atomically."""
p = Path(path)
try:
current = orjson.loads(p.read_bytes())
except (FileNotFoundError, orjson.JSONDecodeError):
current = {}
new = update_fn(current)
p.write_bytes(dumps(new, indent=True))
return new
def load_jsonl(path: str | Path) -> list[Any]:
"""Load a JSON Lines file — one JSON object per line."""
lines = Path(path).read_text(encoding="utf-8").splitlines()
return [orjson.loads(line) for line in lines if line.strip()]
def save_jsonl(path: str | Path, records: list[Any]) -> None:
"""Write records as JSON Lines."""
lines = [orjson.dumps(r).decode() for r in records]
Path(path).write_text("\n".join(lines) + "\n", encoding="utf-8")
# ─────────────────────────────────────────────────────────────────────────────
# 5. FastAPI / Starlette response classes
# ─────────────────────────────────────────────────────────────────────────────
def make_orjson_response_class():
"""
Return an orjson-powered FastAPI JSONResponse subclass.
Usage:
ORJSONResponse = make_orjson_response_class()
app = FastAPI(default_response_class=ORJSONResponse)
@app.get("/data")
async def data():
return {"timestamp": datetime.datetime.utcnow(), "values": [1,2,3]}
"""
try:
from starlette.responses import JSONResponse
class ORJSONResponse(JSONResponse):
media_type = "application/json"
def render(self, content: Any) -> bytes:
return orjson.dumps(
content,
option=(
orjson.OPT_SERIALIZE_NUMPY
| orjson.OPT_NON_STR_KEYS
),
default=default_encoder,
)
return ORJSONResponse
except ImportError:
return None
# ─────────────────────────────────────────────────────────────────────────────
# 6. Redis / cache helpers
# ─────────────────────────────────────────────────────────────────────────────
class OrjsonCache:
"""
Redis (or dict) cache that stores values as orjson bytes.
orjson is faster than json for cache get/set hot paths.
Usage:
cache = OrjsonCache(redis_client)
cache.set("key", {"data": [1, 2, 3]}, ttl=60)
obj = cache.get("key")
"""
def __init__(self, backend, prefix: str = ""):
self._b = backend
self._p = prefix
def _k(self, key: str) -> str:
return f"{self._p}{key}" if self._p else key
def set(self, key: str, value: Any, ttl: int | None = None) -> None:
data = orjson.dumps(value, default=default_encoder)
k = self._k(key)
if hasattr(self._b, "setex") and ttl:
self._b.setex(k, ttl, data)
elif hasattr(self._b, "set"):
self._b.set(k, data)
else:
self._b[k] = data # dict backend
def get(self, key: str, default: Any = None) -> Any:
k = self._k(key)
data = self._b.get(k)
if data is None:
return default
return orjson.loads(data)
def delete(self, key: str) -> None:
k = self._k(key)
if hasattr(self._b, "delete"):
self._b.delete(k)
else:
self._b.pop(k, None)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import json
import time
print("=== Basic usage ===")
obj = {"name": "Alice", "score": 98.5, "active": True, "tags": ["python", "api"]}
b = dumps(obj)
print(f" bytes: {b}")
print(f" loaded: {loads(b)}")
print("\n=== Native types ===")
native = {
"now": datetime.datetime.now(tz=datetime.timezone.utc),
"today": datetime.date.today(),
"uid": uuid.uuid4(),
"duration": datetime.timedelta(hours=1, minutes=30),
}
packed = dumps_extended(native)
print(f" JSON: {packed.decode()}")
print("\n=== Dataclass ===")
@dataclasses.dataclass
class User:
name: str
age: int
created: datetime.datetime
u = User("Bob", 30, datetime.datetime.utcnow())
print(f" {dumps(u).decode()}")
print("\n=== Extended types ===")
ext_obj = {
"price": decimal.Decimal("19.99"),
"features": frozenset(["fast", "safe", "simple"]),
"path": Path("/tmp/data"),
"checksum": b"\xde\xad\xbe\xef",
}
print(f" {dumps_extended_str(ext_obj, indent=True)}")
print("\n=== Options ===")
d = {"z": 1, "a": 2, "m": 3}
print(f" sorted: {dumps_str(d, sort_keys=True)}")
print(f" indented:\n{dumps_str(d, indent=True)}")
print("\n=== Speed comparison ===")
payload = {
"id": 12345,
"items": [{"name": f"item-{i}", "price": i * 0.99} for i in range(50)],
"ts": datetime.datetime.utcnow().isoformat(),
}
N = 50_000
t0 = time.perf_counter()
for _ in range(N):
json.dumps(payload).encode()
json_t = time.perf_counter() - t0
t0 = time.perf_counter()
for _ in range(N):
orjson.dumps(payload)
orjson_t = time.perf_counter() - t0
print(f" json.dumps {N:,}x: {json_t*1000:.1f}ms")
print(f" orjson.dumps {N:,}x: {orjson_t*1000:.1f}ms ({json_t/orjson_t:.1f}x faster)")
For the ujson alternative — ujson is another fast JSON library but hasn’t been actively maintained and has known precision bugs with floats; orjson is maintained, correct, and typically faster; both return different types (ujson returns str, orjson returns bytes — always decode or use .decode() when you need str). For the stdlib json alternative — stdlib json is safe, predictable, and zero-dependency; orjson is 3–10× faster, natively handles dataclasses, datetimes, UUIDs, and numpy arrays without a custom encoder, and produces correct float output; upgrade to orjson whenever JSON serialization is on a hot path. The Claude Skills 360 bundle includes orjson skill sets covering orjson.dumps()/loads() core API, dumps() wrapper with indent/sort_keys options, default_encoder() for Decimal/set/bytes/Path, dumps_extended()/dumps_extended_str(), OPT_SERIALIZE_NUMPY for arrays, serialize_dataframe() pandas helper, load_json_file()/save_json_file()/update_json_file(), load_jsonl()/save_jsonl() JSON Lines, make_orjson_response_class() FastAPI response, and OrjsonCache Redis/dict backend. Start with the free tier to try fast JSON serialization code generation.