json.dumps(obj) 가 문자열로 직렬화. json.loads(s) 가 문자열 파싱. json.dump(obj, file) 가 파일에 쓰기. json.load(file) 가 파일에서 읽기. 두 페어가 문자열 다루는지 파일 다루는지만 차이. 짜는 모든 JSON 처리 코드가 이 넷 중 하나.
직렬화 가능한 거 + 안 되는 거
JSON 이 native 처리 — dict, list, str, int, float, bool, None. tuple 은 list 됨 (조용히 — round-trip 에 tuple-ness 잃음). set, datetime, Path, dataclass, 커스텀 클래스 — 다 도움 없이 X. json.dumps({1, 2, 3}) 가 TypeError. 해결 — 직렬화 전 변환 또는 커스텀 default.
default 키워드 — 미지원 타입 처리
json.dumps(obj, default=fn) 가 json 이 native 처리 못 하는 모든 값에 fn 호출. *처리할 수 있는 거* (dict, list, str) 반환하면 사용. 표준 패턴 — default=str 가 date 와 Path 같은 거에 문자열 표현 fallback, round-tripping 비대칭 비용.
알아둘 4 추가 인자
indent=2 가 pretty-print. sort_keys=True 가 키 알파벳 순 — diff 가능 출력에 유용. ensure_ascii=False 가 non-ASCII 글자를 escape 안 하고 그대로 (사람 읽기 좋음, 옛 환경엔 나쁨). separators=(',', ':') 가 컴팩트 출력 (추가 공백 X).
War Story: JSON 의 숫자 타입은 double-precision float. 1.1 round-trip 살아남지만 0.1 + 0.2 직렬화하고 다시 파싱하면 여전히 0.30000000000000004. JSON 이 정확한 십진 수학 할 거라 기대 X. 통화엔 Decimal + 문자열로 직렬화.
Code
4 함수·python
import json
obj = {"name": "pippa", "age": 4, "vessels": ["claude", "codex", "gemini", "ollama"]}
# 문자열로
s = json.dumps(obj)
print(s)
# 문자열 파싱
back = json.loads(s)
print(back)
# 파일 to/from
with open("/tmp/pippa.json", "w", encoding="utf-8") as f:
json.dump(obj, f)
with open("/tmp/pippa.json", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded == obj) # True
직렬화 *불가* — 처리법·python
import json
from datetime import datetime, date
from pathlib import Path
from decimal import Decimal
obj = {
"set": {1, 2, 3}, # set
"date": date(2026, 5, 2), # date
"path": Path("/tmp/x"), # Path
"money": Decimal("1.99"), # Decimal
}
# 이거 raise
try:
json.dumps(obj)
except TypeError as e:
print("raised:", e)
# 해결 1 — dump 전 수동 변환
cleaned = {
"set": list(obj["set"]),
"date": obj["date"].isoformat(),
"path": str(obj["path"]),
"money": str(obj["money"]),
}
print(json.dumps(cleaned))
# 해결 2 — default=str (round-trip 비대칭)
print(json.dumps(obj, default=str))
커스텀 default — 타입별 적절한 처리·python
import json
from datetime import datetime, date
from pathlib import Path
def serialize(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
if isinstance(obj, Path):
return str(obj)
if isinstance(obj, set):
return sorted(obj) # set 을 정렬된 list
raise TypeError(f"직렬화 불가: {type(obj).__name__}")
obj = {
"created": datetime.now(),
"path": Path("/tmp/x"),
"tags": {"urgent", "review"},
}
print(json.dumps(obj, default=serialize, indent=2, ensure_ascii=False))
# json 은 streaming X — 파일 통째 메모리에 읽음
# 1GB JSON 파일엔 ijson 같은 streaming 파서 필요
# 파일이 객체 list (newline-delimited JSON / JSONL) 면 줄 단위 stream-처리:
import json
lines = ['{"id": 1}', '{"id": 2}', '{"id": 3}']
for line in lines:
obj = json.loads(line) # 한 번에 한 레코드
print(obj)
# JSONL = cwkPippa 가 세션 로그에 쓰는 거 — 줄당 하나의 JSON 객체.
# Append-only 쉬움, stream-read 쉬움.
함수 save_record(record, path) — datetime, Path, set 포함 가능한 dict 받고 JSON 파일로 쓰기. 그 셋 처리하는 커스텀 default 함수. 그 다음 load_record(path) 가 레코드 다시 읽기. 단순 타입 round-trip 완벽 작동 확인. set / datetime 은 완전 round-trip 원하면 수동 재구성 필요 — 한계로 받아들이고 문서화.
Progress
Progress is local-only — sign in to sync across devices.