Làm việc với JSON
Bạn sẽ học được
- Chuyển đổi giữa chuỗi JSON và cấu trúc Python.
- Giữ tiếng Việt với
ensure_ascii=False. - Đọc/ghi file JSON bằng
json.load/json.dump. - Bảng ánh xạ kiểu dữ liệu và các kiểu không serialize được.
- Xử lý JSON hỏng và dữ liệu lồng.
Khái niệm cốt lõi
JSON là định dạng văn bản trao đổi dữ liệu, gần như ánh xạ trực tiếp sang dict/list/str/số/bool/None của Python. Hai hàm cốt lõi:
| Hàm | Chiều | Đầu vào | Đầu ra |
|---|---|---|---|
json.loads(text) | JSON → Python | Chuỗi | Dict/list |
json.dumps(obj) | Python → JSON | Dict/list | Chuỗi |
import json
raw = '{"ten": "An", "tuoi": 28, "ky_nang": ["SQL", "Python"]}'
data = json.loads(raw)
print(data["ten"], data["ky_nang"][1])An PythonSau loads, data là dict thật — truy cập, duyệt, sửa như mọi dict khác. Không còn là chuỗi.
dumps và tiếng Việt
Mặc định json.dumps escape ký tự không phải ASCII, nên tiếng Việt thành ạ:
print(json.dumps({"ten": "An", "tuoi": 28}, ensure_ascii=False)){"ten": "An", "tuoi": 28}Luôn đặt ensure_ascii=False khi dữ liệu có tiếng Việt — file dễ đọc, nhỏ hơn, và hiển thị đúng trong log. Thêm indent=2 để dễ đọc:
print(json.dumps({"ten": "An", "tuoi": 28}, ensure_ascii=False, indent=2)){
"ten": "An",
"tuoi": 28
}indent chỉ nên dùng cho file cấu hình hoặc khi debug. Dữ liệu gửi qua mạng nên để một dòng để tiết kiệm byte.
Đọc và ghi file
with open("order.json", encoding="utf-8") as f:
data = json.load(f)
with open("order.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)load/dump làm việc với file; loads/dumps làm việc với chuỗi. Nhớ encoding="utf-8" ở cả hai chiều, nếu không tiếng Việt có thể lỗi trên Windows.
Bảng ánh xạ kiểu
| Python | JSON | Ghi chú |
|---|---|---|
dict | Object | Khóa phải là chuỗi |
list, tuple | Array | Tuple thành array khi ghi |
str | String | |
int, float | Number | |
True / False | true / false | |
None | null | |
datetime, Decimal | — | Không serialize được |
Ví dụ lỗi rất hay gặp:
from datetime import date
json.dumps({"ngay": date(2026, 1, 1)})TypeError: Object of type date is not JSON serializableCách sửa: chuyển sang chuỗi ISO trước:
print(json.dumps({"ngay": date(2026, 1, 1).isoformat()}, ensure_ascii=False)){"ngay": "2026-01-01"}Với Decimal (tiền tệ), tương tự: str(decimal_value) hoặc float(...) tuỳ nhu cầu chính xác. Với cấu trúc phức tạp, truyền default= cho dumps:
def chuyen(obj):
if isinstance(obj, date):
return obj.isoformat()
raise TypeError(f"Không hỗ trợ {type(obj).__name__}")
json.dumps({"ngay": date(2026, 1, 1)}, default=chuyen)JSON hỏng
loads ném json.JSONDecodeError (lớp con của ValueError) khi chuỗi sai cú pháp:
try:
json.loads('{"ten": "An",}')
except json.JSONDecodeError as e:
print("JSON lỗi:", e)Các lỗi phổ biến: dấu phẩy thừa cuối object/array, dùng nháy đơn thay nháy kép, khóa không đặt trong nháy, thiếu ngoặc đóng. JSON không cho comment và không cho dấu phẩy cuối — khác Python dict.
Khi nhận dữ liệu từ API, luôn coi JSON là dữ liệu không tin cậy: kiểm tra kiểu sau khi parse, và bắt JSONDecodeError ở biên hệ thống.
Duyệt JSON lồng
Sau khi parse, JSON là dict/list lồng nhau. Dùng lại kỹ thuật chương trước:
payload = {
"order": {"id": 1001, "items": [{"sku": "A1", "qty": 2}, {"sku": "B2", "qty": 1}]}
}
for item in payload["order"]["items"]:
print(item["sku"], item["qty"])A1 2
B2 1Nếu JSON đến từ nguồn ngoài, dùng .get() từng tầng thay vì tin cấu trúc cố định. Một trường đổi tên ở API là đủ làm KeyError toàn bộ pipeline.
Thực hành
loadsmột chuỗi JSON có tiếng Việt, in một trường.dumpsvớiensure_ascii=False, so sánh khi bỏ tham số.- Ghi dict ra file
order.json, mở bằng editor để xem. - Thử serialize
datevà đọcTypeError. - Sửa bằng
.isoformat(), sau đó bằngdefault=. - Parse một chuỗi JSON sai (dấu phẩy cuối) và bắt
JSONDecodeError.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
Tiếng Việt thành \u... | Mặc định escape ASCII | ensure_ascii=False |
JSONDecodeError | Cú pháp sai | Kiểm tra nháy kép, dấu phẩy cuối |
TypeError: not JSON serializable | date, Decimal, set | Chuyển sang chuỗi/số trước |
KeyError sau parse | Cấu trúc khác dự kiến | Dùng get và kiểm tra kiểu |
| File lỗi font | Thiếu encoding="utf-8" | Thêm ở open |
| Số thực mất chính xác | Dùng float cho tiền | Dùng Decimal/chuỗi ở biên |
dump rồi load ra khác | Tuple thành list | Nhớ JSON không có tuple |
Tóm tắt
loads/dumpscho chuỗi;load/dumpcho file.- Luôn
ensure_ascii=Falsevới tiếng Việt;indentkhi cần đọc. - JSON ánh xạ dict/list/str/số/bool/None;
datevàDecimalcần chuyển đổi. - Bắt
JSONDecodeErrorkhi parse dữ liệu ngoài. - Sau parse vẫn phải kiểm tra cấu trúc — JSON hợp lệ không có nghĩa là đúng schema.
Dùng ← → để chuyển bài