JSON và Python nói chung một ngôn ngữ cấu trúc
JSON là định dạng mặc định cho API, tệp cấu hình và trao đổi dữ liệu trên web. May mắn cho lập trình viên Python, một object JSON tương ứng trực tiếp với một dict của Python, một array JSON với một list, một string JSON với một str. Sự tương đồng đó là lý do việc đọc và ghi JSON trong Python chỉ cần hai dòng code.
Module json của thư viện chuẩn xử lý cả hai chiều.
Phân tích một chuỗi JSON
json.loads(text), tức "load string", nhận một chuỗi có định dạng JSON và trả về đối tượng Python mà chuỗi đó biểu diễn:
Kết quả là một dict thông thường. Bạn truy cập các khóa như với mọi dict khác: không có lớp bọc JSON đặc biệt nào, cũng không có phương thức .parse() trên kết quả.
Nếu chuỗi không phải JSON hợp lệ, json.loads sẽ ném ra json.JSONDecodeError. Thông báo lỗi có kèm dòng và cột gây lỗi, thường là đủ để bạn phát hiện một dấu phẩy bị thiếu hoặc một dấu nháy chưa được thoát.
Ghi JSON thành chuỗi
json.dumps(data), tức "dump string", làm điều ngược lại: nó nhận một đối tượng Python và trả về một chuỗi JSON:
True, False và None của Python được tự động chuyển thành true, false và null của JSON. Số và chuỗi giữ nguyên. List trở thành array, dict trở thành object.
In JSON đẹp mắt
Đầu ra mặc định của json.dumps rất gọn, hợp để truyền qua mạng nhưng khó đọc với con người. Truyền indent=2 để có kết quả dễ đọc hơn:
Vài tùy chọn khác của dumps nên biết:
sort_keys=True: sắp xếp các khóa của object theo thứ tự bảng chữ cái. Hữu ích khi cần đầu ra ổn định (tệp cấu hình, dữ liệu kiểm thử, so sánh khác biệt).ensure_ascii=False: ghi các ký tự ngoài ASCII (é, ü, 中) đúng như chúng là, thay vì dạng thoát\u. Thường là lựa chọn đúng cho tệp UTF-8.separators=(",", ":"): đầu ra gọn nhất có thể. Kết hợp vớiensure_ascii=False, bạn có JSON UTF-8 nhỏ gọn nhất.
Đọc JSON từ tệp
json.load(file), không có s, đọc trực tiếp từ một đối tượng tệp. Cách dùng phổ biến là kết hợp với with open:
import json
with open("config.json") as f:
config = json.load(f)
print(config["version"])
Không cần đọc cả tệp vào một chuỗi trước; json.load đọc thẳng qua đối tượng tệp.
Ghi JSON vào tệp
json.dump(data, file) là phiên bản dùng để ghi tệp:
import json
data = {"created": "2026-01-01", "items": [1, 2, 3]}
with open("state.json", "w") as f:
json.dump(data, f, indent=2)
Tùy chọn indent cũng dùng được ở đây. Mở tệp kết quả ra, bạn sẽ thấy một tài liệu JSON được định dạng gọn gàng.
Đọc response JSON từ API
Hầu hết thư viện HTTP trả cho bạn byte hoặc văn bản; bạn gọi json.loads để biến chúng thành dữ liệu dùng được:
import json
import urllib.request
with urllib.request.urlopen("https://api.example.com/users/1") as response:
text = response.read().decode("utf-8")
user = json.loads(text)
print(user["name"])
Thư viện requests (có trang riêng) bỏ bớt một bước: response của nó có phương thức .json() gọi json.loads giúp bạn.
JSON biểu diễn được gì và không được gì
Hệ thống kiểu của JSON hẹp hơn của Python. Bảng tương ứng theo cả hai chiều:
| Python | JSON |
|---|---|
dict | object |
list, tuple | array |
str | chuỗi |
int, float | số |
True | true |
False | false |
None | null |
Tuple khi chuyển đi rồi chuyển về sẽ thành list, mất đi tính chất tuple. Set, lớp tùy chỉnh và đối tượng datetime mặc định không tuần tự hóa được; bạn sẽ gặp TypeError: Object of type X is not JSON serializable.
Xử lý datetime và đối tượng tùy chỉnh
Có hai cách phổ biến.
Chuyển sang cấu trúc an toàn với JSON trước. Tự biến đổi trong code của bạn, rồi tuần tự hóa một dict thông thường:
Truyền một hàm default=. json.dumps gọi hàm này cho mỗi giá trị mà nó không biết cách tuần tự hóa:
Khi đọc dữ liệu trở lại, bạn phải tự chuyển các chuỗi ISO đó thành datetime, vì JSON không nhớ ban đầu chúng là gì.
Chuyển một dict qua JSON rồi trở lại
Kiểm tra nhanh rằng dict vẫn nguyên vẹn:
Các giá trị bằng nhau, nhưng là hai đối tượng khác nhau. Đó thường là điều bạn muốn: json.dumps + json.loads là cách rẻ để tạo bản sao sâu (deep copy) của bất kỳ cấu trúc nào tương thích với JSON.
Một ví dụ thực tế
Một script nhỏ nạp tệp cấu hình JSON, cập nhật một trường và lưu lại:
import json
from pathlib import Path
config_path = Path("settings.json")
# Load (with a default if the file doesn't exist).
if config_path.exists():
config = json.loads(config_path.read_text())
else:
config = {"theme": "dark", "last_opened": None}
# Update.
config["last_opened"] = "2026-01-15"
# Save, pretty-printed.
config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False))
Đó là một chu trình "đọc, sửa, ghi" JSON hoàn chỉnh chỉ trong khoảng mười mấy dòng.
Vài thói quen nên có
- Luôn dùng
with open(...)cho tệp. JSON chỉ là văn bản; mọi quy tắc xử lý tệp đều áp dụng. - Ưu tiên
indent=2cho tệp mà con người đọc: cấu hình, dữ liệu kiểm thử, dữ liệu xuất ra. Bỏ qua nó với dữ liệu truyền qua mạng, nơi độ gọn quan trọng. - Đặt
ensure_ascii=Falsecho đầu ra UTF-8 để tên có dấu hoặc ký tự ngoài bảng chữ Latin vẫn dễ đọc. - Kiểm tra bằng
try/except json.JSONDecodeErrorkhi phân tích dữ liệu không phải do bạn tạo ra.
Tiếp theo
JSON xử lý dữ liệu dạng khóa và giá trị. Công cụ tiếp theo trong cùng chương là CSV: phần hỗ trợ có sẵn của Python cho định dạng bảng đứng sau hầu hết các tệp xuất từ bảng tính mà bạn sẽ gặp.
Câu hỏi thường gặp
Làm sao để phân tích JSON trong Python?
Dùng json.loads(text) cho chuỗi JSON hoặc json.load(file) cho đối tượng tệp. Cả hai đều trả về một dict Python (hoặc list, tùy vào JSON). Ví dụ: data = json.loads('{"name": "Rosa"}'), khi đó data['name'] có giá trị 'Rosa'.
Làm sao để chuyển một dictionary Python sang JSON?
json.dumps(my_dict) trả về một chuỗi JSON. json.dump(my_dict, file) ghi thẳng vào một đối tượng tệp. Truyền indent=2 để có đầu ra trình bày đẹp: json.dumps(data, indent=2).
json.loads và json.load khác nhau thế nào?
loads (có s) nhận một chuỗi. load (không có s) nhận một đối tượng tệp. Tương tự với dumps và dump. Chữ s là viết tắt của string (chuỗi); đó là cách dễ nhất để nhớ cái nào là cái nào.
Làm sao để xử lý ngày tháng và đối tượng tùy chỉnh trong JSON?
JSON không có kiểu ngày tháng, nên hãy truyền ngày tháng dưới dạng chuỗi ISO-8601 và tự phân tích lại khi đọc. Với lớp tùy chỉnh, hãy truyền một hàm default= cho json.dumps để trả về dạng biểu diễn an toàn với JSON, hoặc tự chuyển sang dict trước khi tuần tự hóa.