DHDataHọc
Lý thuyếtBài 27/304 phút đọc+10 XP

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àmChiềuĐầu vàoĐầu ra
json.loads(text)JSON → PythonChuỗiDict/list
json.dumps(obj)Python → JSONDict/listChuỗi
code
import json

raw = '{"ten": "An", "tuoi": 28, "ky_nang": ["SQL", "Python"]}'
data = json.loads(raw)
print(data["ten"], data["ky_nang"][1])
code
An Python

Sau 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 ạ:

code
print(json.dumps({"ten": "An", "tuoi": 28}, ensure_ascii=False))
code
{"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:

code
print(json.dumps({"ten": "An", "tuoi": 28}, ensure_ascii=False, indent=2))
code
{
  "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

code
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

PythonJSONGhi chú
dictObjectKhóa phải là chuỗi
list, tupleArrayTuple thành array khi ghi
strString
int, floatNumber
True / Falsetrue / false
Nonenull
datetime, Decimal—Không serialize được

Ví dụ lỗi rất hay gặp:

code
from datetime import date

json.dumps({"ngay": date(2026, 1, 1)})
code
TypeError: Object of type date is not JSON serializable

Cách sửa: chuyển sang chuỗi ISO trước:

code
print(json.dumps({"ngay": date(2026, 1, 1).isoformat()}, ensure_ascii=False))
code
{"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:

code
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:

code
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:

code
payload = {
    "order": {"id": 1001, "items": [{"sku": "A1", "qty": 2}, {"sku": "B2", "qty": 1}]}
}

for item in payload["order"]["items"]:
    print(item["sku"], item["qty"])
code
A1 2
B2 1

Nế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

  1. loads một chuỗi JSON có tiếng Việt, in một trường.
  2. dumps với ensure_ascii=False, so sánh khi bỏ tham số.
  3. Ghi dict ra file order.json, mở bằng editor để xem.
  4. Thử serialize date và đọc TypeError.
  5. Sửa bằng .isoformat(), sau đó bằng default=.
  6. 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ứngNguyên nhânCách sửa
Tiếng Việt thành \u...Mặc định escape ASCIIensure_ascii=False
JSONDecodeErrorCú pháp saiKiểm tra nháy kép, dấu phẩy cuối
TypeError: not JSON serializabledate, Decimal, setChuyển sang chuỗi/số trước
KeyError sau parseCấu trúc khác dự kiếnDùng get và kiểm tra kiểu
File lỗi fontThiếu encoding="utf-8"Thêm ở open
Số thực mất chính xácDùng float cho tiềnDùng Decimal/chuỗi ở biên
dump rồi load ra khácTuple thành listNhớ JSON không có tuple

Tóm tắt

  • loads/dumps cho chuỗi; load/dump cho file.
  • Luôn ensure_ascii=False với tiếng Việt; indent khi cần đọc.
  • JSON ánh xạ dict/list/str/số/bool/None; date và Decimal cần chuyển đổi.
  • Bắt JSONDecodeError khi 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