JSON Formatter Python — Hướng dẫn json.dumps()

·Backend Developer·Đánh giá bởiDmitri Volkov·Đã xuất bản

Sử dụng Định dạng và Làm đẹp JSON miễn phí trực tiếp trên trình duyệt — không cần cài đặt.

Dùng thử Định dạng và Làm đẹp JSON trực tuyến →

Khi tôi debug một Python API client, thứ đầu tiên tôi nghĩ đến là python in đẹp json — chỉ một lời gọi json.dumps(data, indent=4) biến một blob một dòng khó đọc thành chuỗi có thể duyệt ngay lập tức. Module tích hợp sẵn json của Python xử lý hoàn toàn trong thư viện chuẩn — không cần cài thư viện bên thứ ba. Nếu chỉ cần kết quả nhanh mà không viết code, JSON Formatter của ToolDeck làm điều đó ngay lập tức. Hướng dẫn này bao gồm mọi phương thức thực tế: json.dumps() với tất cả tham số, pprint, orjson cho định dạng hiệu năng cao, CLI json.tool, và các tình huống thực tế như định dạng phản hồi API và đọc từ đĩa — tất cả với code tương thích Python 3.8+. Ngoài ra còn đề cập serialize các kiểu tùy chỉnh như datetime UUID, streaming file hàng gigabyte với ijson, và tô sáng cú pháp trên terminal với rich.

Những điểm chính
  • json.dumps(data, indent=4) được tích hợp sẵn trong stdlib của Python từ phiên bản 2.6 — không cần cài đặt.
  • Truyền ensure_ascii=False bất cứ khi nào dữ liệu chứa chữ có dấu, ký tự CJK hoặc emoji.
  • Với datetime, UUID hoặc các lớp tùy chỉnh, dùng tham số default= hoặc kế thừa json.JSONEncoder.
  • separators=(',', ':') loại bỏ toàn bộ khoảng trắng — dùng cho truyền qua mạng hoặc nhúng URL.
  • orjson nhanh hơn 5–10× so với stdlib và xử lý gốc datetimeuuid.UUID.
  • pprint.pprint() xuất cú pháp Python (True/None), không phải JSON hợp lệ — đừng dùng cho file hay phản hồi API.
  • Với file JSON lớn hơn 50 MB, dùng streaming với ijson thay vì json.load() để tránh MemoryError.

JSON In Đẹp (Pretty Printing) Là Gì?

In đẹp biến đổi một chuỗi JSON dày đặc, đã được thu nhỏ thành định dạng dễ đọc với thụt lề nhất quán và xuống dòng. Sự biến đổi hoàn toàn mang tính thẩm mỹ: dữ liệu giống hệt nhau, chỉ có cách trình bày thay đổi. Module json của Python xử lý hoàn toàn trong thư viện chuẩn — không cần cài thêm gì.

Before · json
After · json
{"id":"usr_9f3a2b","name":"Nguyễn Văn An","roles":["admin","editor"],"prefs":{"theme":"dark","lang":"vi"}}
{
    "id": "usr_9f3a2b",
    "name": "Nguyễn Văn An",
    "roles": [
        "admin",
        "editor"
    ],
    "prefs": {
        "theme": "dark",
        "lang": "vi"
    }
}

json.dumps() — Cách Tiêu Chuẩn để Định Dạng JSON

json.dumps() là một phần của thư viện chuẩn Python từ Python 2.6 — chỉ cần import json, không cần cài thêm. Nó serialize bất kỳ đối tượng Python tương thích JSON thành chuỗi có định dạng. Tham số chính là indent: đặt thành 4 (hoặc 2) để có đầu ra dễ đọc.

Python 3.8+ — ví dụ tối giản
import json

nguoi_dung = {
    "id": "usr_9f3a2b",
    "name": "Nguyễn Văn An",
    "roles": ["admin", "editor"],
    "prefs": {"theme": "dark", "lang": "vi"}
}

print(json.dumps(nguoi_dung, indent=4, ensure_ascii=False))
# Đầu ra:
# {
#     "id": "usr_9f3a2b",
#     "name": "Nguyễn Văn An",
#     "roles": [
#         "admin",
#         "editor"
#     ],
#     "prefs": {
#         "theme": "dark",
#         "lang": "vi"
#     }
# }

Trong môi trường production bạn thường sẽ muốn sort_keys=True (đầu ra nhất quán qua các lần chạy) và ensure_ascii=False (giữ nguyên ký tự tiếng Việt):

Python 3.8+ — với sort_keys và ensure_ascii
import json

phan_hoi_api = {
    "timestamp": "2024-05-01T10:30:00Z",
    "status": "thanh_cong",
    "data": {
        "user_id": "usr_9f3a2b",
        "ten_hien_thi": "Trần Thị Bình",
        "thanh_pho": "Hà Nội",
        "diem": 4892.5,
        "tags": ["python", "backend", "api"]
    }
}

print(json.dumps(phan_hoi_api, indent=4, sort_keys=True, ensure_ascii=False))
# Đầu ra (khóa được sắp xếp, ký tự tiếng Việt được giữ nguyên):
# {
#     "data": {
#         "diem": 4892.5,
#         "tags": ["api", "backend", "python"],
#         "ten_hien_thi": "Trần Thị Bình",
#         "thanh_pho": "Hà Nội",
#         "user_id": "usr_9f3a2b"
#     },
#     "status": "thanh_cong",
#     "timestamp": "2024-05-01T10:30:00Z"
# }
Lưu ý:json.dumps() trả về một chuỗi. Để ghi JSON được định dạng trực tiếp vào file, dùng json.dump(data, f, indent=4) (không có s) — nó ghi vào đối tượng file và tránh tạo chuỗi trung gian trong bộ nhớ.

Tham chiếu tham số json.dumps()

Tất cả tham số đều tùy chọn ngoại trừ chính đối tượng. Các giá trị mặc định tạo ra JSON gọn, an toàn ASCII — truyền tham số rõ ràng để có đầu ra dễ đọc.

Tham số
Kiểu
Mặc định
Mô tả
obj
any
Đối tượng Python cần serialize thành chuỗi JSON.
indent
int | str | None
None
Số khoảng trắng mỗi cấp thụt lề. None = một dòng gọn, 0 = chỉ xuống dòng, 4 = tiêu chuẩn.
sort_keys
bool
False
Sắp xếp khóa dictionary theo thứ tự chữ cái ở mọi cấp lồng nhau.
ensure_ascii
bool
True
Escape ký tự non-ASCII thành \uXXXX. Đặt False để giữ nguyên ký tự Unicode.
separators
tuple | None
None
Cặp (sep_item, sep_key). Dùng (",", ":") để có đầu ra gọn nhất không có khoảng trắng.
default
callable | None
None
Gọi cho các kiểu không thể serialize mặc định. Raise TypeError để từ chối giá trị.
allow_nan
bool
True
Serialize float("nan") và float("inf") như literal JS. Đặt False để raise ValueError.

Đầu ra JSON Gọn với Tham số separators

Mặc định json.dumps() phân tách các mục bằng ", " và khóa từ giá trị bằng ": ". Tham số separators ghi đè cả hai. Truyền (',', ':') loại bỏ toàn bộ khoảng trắng để tạo ra JSON hợp lệ gọn nhất có thể — hữu ích cho truyền qua mạng, nhúng URL hoặc lưu JSON trong cột cơ sở dữ liệu khi mỗi byte đều quan trọng.

Python 3.8+
import json

payload = {
    "endpoint": "/api/v2/su-kien",
    "filters": {"status": "hoat_dong", "limit": 100},
    "sort": "desc"
}

# Mặc định — có khoảng trắng sau dấu phân tách (dễ đọc)
mac_dinh = json.dumps(payload, ensure_ascii=False)
# {"endpoint": "/api/v2/su-kien", "filters": {"status": "hoat_dong", "limit": 100}, "sort": "desc"}
# len = 91

# Gọn — không có khoảng trắng
gon = json.dumps(payload, separators=(',', ':'), ensure_ascii=False)
# {"endpoint":"/api/v2/su-kien","filters":{"status":"hoat_dong","limit":100},"sort":"desc"}
# len = 83  (tiết kiệm 9%; lợi ích tăng với payload lồng nhau sâu hơn)

# Gọn + sắp xếp khóa cho cache key hoặc content hash tái hiện
chuan_tac = json.dumps(payload, separators=(',', ':'), sort_keys=True, ensure_ascii=False)
print(chuan_tac)
# {"endpoint":"/api/v2/su-kien","filters":{"limit":100,"status":"hoat_dong"},"sort":"desc"}
Lưu ý:Khi truyền indent= cùng với separators=, đối số separators chỉ điều khiển dấu phân tách inline — xuống dòng và thụt lề từ indent vẫn được giữ lại. Nếu muốn đầu ra một dòng gọn, bỏ qua indent (hoặc truyền None) và đặt separators=(',', ':').

Serialize Đối tượng Python Tùy chỉnh với Tham số default

Module json tiêu chuẩn serialize dict, list, string, số, boolean và None — nhưng raiseTypeError với bất kỳ kiểu nào khác. Hai thủ phạm phổ biến nhất trong code production là đối tượng datetime UUID.

Python 3.8+ — TypeError khi không có xử lý tùy chỉnh
import json
from datetime import datetime, timezone
import uuid

don_hang = {
    "don_hang_id": uuid.uuid4(),              # ❌ TypeError: UUID is not JSON serializable
    "dat_luc": datetime.now(timezone.utc),    # ❌ TypeError: datetime is not JSON serializable
    "tong_vnd": 3500000,
    "items": ["goi-pro", "addon-luu-tru"]
}

json.dumps(don_hang)  # raise TypeError

Cách 1 — tham số default=

Truyền một callable cho default=. json.dumps() gọi nó cho bất kỳ đối tượng nào nó không thể xử lý. Trả về một biểu diễn có thể serialize, hoặc raise TypeError cho các kiểu bạn không hỗ trợ rõ ràng — đừng bao giờ bỏ qua các kiểu không xác định.

Python 3.8+
import json
from datetime import datetime, timezone, date
import uuid
from decimal import Decimal

def json_mac_dinh(obj):
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    if isinstance(obj, uuid.UUID):
        return str(obj)
    if isinstance(obj, Decimal):
        return float(obj)
    raise TypeError(f"Kiểu {type(obj).__name__!r} không thể serialize thành JSON")

don_hang = {
    "don_hang_id": uuid.uuid4(),
    "dat_luc": datetime(2024, 5, 1, 10, 30, 0, tzinfo=timezone.utc),
    "tong_vnd": Decimal("3500000"),
    "items": ["goi-pro", "addon-luu-tru"]
}

print(json.dumps(don_hang, indent=4, default=json_mac_dinh, ensure_ascii=False))
# {
#     "don_hang_id": "a3f1c2d4-e5b6-7890-abcd-ef1234567890",
#     "dat_luc": "2024-05-01T10:30:00+00:00",
#     "tong_vnd": 3500000.0,
#     "items": ["goi-pro", "addon-luu-tru"]
# }

Cách 2 — kế thừa json.JSONEncoder

Để có logic encoding có thể tái sử dụng chia sẻ qua nhiều module, kế thừa json.JSONEncoder gọn gàng hơn là truyền hàm default khắp nơi. Override phương thức default và gọi super().default(obj) làm fallback cuối — điều này giữ nguyên hành vi lỗi đúng cho các kiểu không được hỗ trợ.

Python 3.8+
import json
from datetime import datetime, timezone
import uuid
from decimal import Decimal

class AppEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, uuid.UUID):
            return str(obj)
        if isinstance(obj, Decimal):
            return float(obj)
        return super().default(obj)  # raise TypeError cho các kiểu không xác định

don_hang = {
    "don_hang_id": uuid.uuid4(),
    "dat_luc": datetime(2024, 5, 1, 10, 30, 0, tzinfo=timezone.utc),
    "tong_vnd": Decimal("3500000"),
}

# Truyền lớp encoder qua cls=
print(json.dumps(don_hang, indent=4, cls=AppEncoder, ensure_ascii=False))
# Đầu ra giống với cách dùng default= ở trên
Lưu ý:Luôn gọi super().default(obj) (hoặc raise TypeError rõ ràng) cho các kiểu không nhận ra. Trả về str(obj) lặng lẽ cho mọi thứ sẽ làm hỏng các đối tượng đáng lẽ phải raise lỗi — một lỗi khó tìm trong môi trường production.

Giải mã ngược — object_hook

Encoding chỉ là một nửa câu chuyện. Để tái tạo đối tượng Python tùy chỉnh từ JSON, truyền hàm object_hook cho json.loads() hoặc json.load(). Hook được gọi cho mỗi đối tượng JSON được giải mã (dict) và có thể trả về bất kỳ giá trị Python nào thay thế — cho phép vòng tròn encode ↔ decode đầy đủ.

Python 3.8+
import json
from datetime import datetime
from dataclasses import dataclass

@dataclass
class SuKien:
    ten: str
    xay_ra_luc: datetime
    user_id: str

def encode_su_kien(obj):
    if isinstance(obj, SuKien):
        return {
            "__loai__": "SuKien",
            "ten": obj.ten,
            "xay_ra_luc": obj.xay_ra_luc.isoformat(),
            "user_id": obj.user_id,
        }
    raise TypeError(f"Không thể serialize {type(obj)}")

def decode_su_kien(d):
    if d.get("__loai__") == "SuKien":
        return SuKien(
            ten=d["ten"],
            xay_ra_luc=datetime.fromisoformat(d["xay_ra_luc"]),
            user_id=d["user_id"],
        )
    return d

# Encode
su_kien = SuKien("login", datetime(2024, 5, 1, 10, 30), "usr_9f3a2b")
json_str = json.dumps(su_kien, default=encode_su_kien, indent=4)

# Giải mã lại thành instance SuKien
phuc_hoi = json.loads(json_str, object_hook=decode_su_kien)
print(type(phuc_hoi))           # <class 'SuKien'>
print(phuc_hoi.xay_ra_luc)     # 2024-05-01 10:30:00
Lưu ý:object_hook được gọi cho mọi dict lồng nhau trong tài liệu — không chỉ cấp trên cùng. Hãy bao gồm trường phân biệt (như "__loai__") để hook có thể phân biệt đối tượng tùy chỉnh của bạn với dict thông thường cần giữ nguyên.

pprint — Module Thay thế (và Khi Nào Không Nên Dùng)

Module pprint của Python (pretty printer) định dạng cấu trúc dữ liệu Python để dễ đọc trong terminal. Nó hoạt động trên các đối tượng Python đã parse, không phải trên chuỗi JSON — và đầu ra dùng cú pháp Python, không phải cú pháp JSON.

Python 3.8+
import json, pprint

raw = '{"sensor_id":"s-441","readings":[23.1,23.4,22.9],"unit":"celsius","active":true}'
data = json.loads(raw)

# pprint — repr Python hợp lệ, KHÔNG phải JSON hợp lệ
pprint.pprint(data, sort_dicts=False)
# {'sensor_id': 's-441',
#  'readings': [23.1, 23.4, 22.9],
#  'unit': 'celsius',
#  'active': True}        ← Python True, không phải JSON true

# json.dumps — JSON hợp lệ
print(json.dumps(data, indent=4))
# {
#     "sensor_id": "s-441",
#     "readings": [23.1, 23.4, 22.9],
#     "unit": "celsius",
#     "active": true      ← JSON hợp lệ
# }
Cảnh báo:Đừng bao giờ gửi đầu ra pprint đến một API endpoint hay ghi vào file .json — nó sẽ làm hỏng bất kỳ bộ phân tích JSON nào mong đợi cú pháp chuẩn. Dùng json.dumps(indent=4) cho mọi đầu ra phải là JSON hợp lệ.

Khi pprint có ý nghĩa: kiểm tra nhanh đối tượng Python trong REPL hoặc log debug, đặc biệt khi đối tượng chứa các kiểu không thể serialize JSON (set, instance lớp tùy chỉnh, dataclass trước khi chuyển đổi).

Cách In Đẹp Phản hồi JSON từ Requests

Tình huống thực tế phổ biến nhất: bạn có file JSON trên đĩa hoặc phản hồi HTTP từ API, và muốn định dạng để debug hoặc logging. Cả hai trường hợp đều dùng cùng cách tiếp cận — parse thành dict Python, rồi định dạng với json.dumps().

Đọc từ file

Python 3.8+
import json

try:
    with open("config.json", "r", encoding="utf-8") as f:
        data = json.load(f)

    # In đẹp ra console
    print(json.dumps(data, indent=4, ensure_ascii=False))

    # Hoặc ghi phiên bản đã định dạng trở lại đĩa
    with open("config.dep.json", "w", encoding="utf-8") as f:
        json.dump(data, f, indent=4, ensure_ascii=False)

except json.JSONDecodeError as e:
    print(f"JSON không hợp lệ: {e}")
except FileNotFoundError:
    print(f"Không tìm thấy file: config.json")

Định dạng phản hồi API

Python 3.8+ (yêu cầu: pip install requests)
import json, requests
from requests.exceptions import HTTPError, ConnectionError, Timeout

def in_dep_api(url: str) -> None:
    try:
        resp = requests.get(url, timeout=10)
        resp.raise_for_status()
        print(json.dumps(resp.json(), indent=4, ensure_ascii=False))
    except HTTPError as e:
        print(f"HTTP {e.response.status_code}: {e}")
    except (ConnectionError, Timeout) as e:
        print(f"Lỗi mạng: {e}")
    except json.JSONDecodeError:
        print(f"Body phản hồi không phải JSON:\n{resp.text[:500]}")

in_dep_api("https://api.github.com/repos/python/cpython")
Lưu ý:response.json() đã parse body phản hồi rồi — không cần gọi json.loads() thêm. Luôn thêm raise_for_status() trước khi truy cập .json() để bắt lỗi 4xx/5xx trước khi chúng gây ra lỗi parse khó hiểu.

In Đẹp từ Dòng Lệnh

Python đi kèm với json.tool, một module CLI để định dạng JSON thẳng từ terminal — không cần viết script Python. Có sẵn trên bất kỳ máy nào cài Python.

bash
# Định dạng file local
python -m json.tool config.json

# Pipe phản hồi API qua formatter
curl -s https://api.github.com/users/gvanrossum | python -m json.tool

# Định dạng từ stdin
echo '{"service":"api-gateway","version":"2.1.0","healthy":true}' | python -m json.tool

# Sắp xếp khóa theo thứ tự chữ cái
python -m json.tool --sort-keys data.json

# Thụt lề tùy chỉnh (Python 3.9+)
python -m json.tool --indent 2 data.json
Lưu ý:Python 3.9 đã thêm cờ --indent --no-indent. Để lọc JSON trên terminal mạnh hơn, hãy xem xét jq — nhưng python -m json.tool đáp ứng nhu cầu định dạng với không phụ thuộc thêm.

Nếu bạn không ở trong terminal — dán phản hồi từ Postman hoặc file log — JSON Formatter của ToolDeck cho phép dán, định dạng và sao chép trong một bước với syntax highlighting và validation tích hợp sẵn.

Thư viện Thay thế: orjson và rich

orjson — Nhanh hơn 5–10× với Hỗ trợ Kiểu Gốc

Module json tiêu chuẩn đủ nhanh cho hầu hết trường hợp, nhưng nếu bạn serialize hàng nghìn đối tượng mỗi giây — logging pipeline, API thông lượng cao, xuất dữ liệu lớn — orjson nhanh hơn 5–10×. Nó cũng xử lý gốc các kiểu mà thư viện chuẩn không thể serialize không có hàm default tùy chỉnh: datetime, uuid.UUID, mảng numpy và dataclass.

bash — cài đặt
pip install orjson
Python 3.8+
import orjson
from datetime import datetime, timezone
import uuid

su_kien = {
    "event_id": uuid.uuid4(),                  # không cần str() — orjson xử lý UUID
    "timestamp": datetime.now(timezone.utc),   # không cần isoformat()
    "service": "auth-service",
    "level": "INFO",
    "payload": {
        "user_id": "usr_9f3a2b",
        "action": "login",
        "ip": "192.168.1.42",
        "latency_ms": 34
    }
}

# orjson.dumps trả về bytes; .decode() chuyển thành str
print(orjson.dumps(su_kien, option=orjson.OPT_INDENT_2).decode())
# {
#   "event_id": "a3f1c2d4-e5b6-7890-abcd-ef1234567890",
#   "timestamp": "2024-05-01T10:30:00+00:00",
#   "service": "auth-service",
#   ...
# }

Hai điều cần biết: orjson.dumps() trả về bytes, không phải string — gọi .decode() nếu bạn cần string. Nó chỉ hỗ trợ thụt lề 2 khoảng trắng qua OPT_INDENT_2; để có đầu ra 4 khoảng trắng dùng json.dumps(indent=4) tiêu chuẩn.

rich — Tô Sáng Cú pháp Trên Terminal

Nếu bạn thường xuyên kiểm tra JSON trên terminal hoặc REPL, rich hiển thị đầu ra có màu sắc, tô sáng cú pháp giúp các cấu trúc lồng nhau sâu dễ đọc ngay. Khóa, string, số và boolean mỗi loại có màu riêng — dễ duyệt hơn nhiều so với văn bản đơn sắc. Đây là công cụ debug, không dùng cho serialization production.

bash — cài đặt
pip install rich
Python 3.8+
from rich import print_json
import json

# print_json() nhận chuỗi JSON
raw = '{"event":"login","user_id":"usr_9f3a2b","timestamp":"2024-05-01T10:30:00Z","success":true,"meta":{"ip":"192.168.1.42","attempts":1}}'
print_json(raw)

# Để in đẹp dict Python, chuyển thành string trước
data = {
    "status": "thanh_cong",
    "count": 42,
    "tags": ["python", "api", "backend"]
}
print_json(json.dumps(data, ensure_ascii=False))
Cảnh báo:rich.print_json() xuất mã escape ANSI cho màu terminal — đừng bao giờ bắt đầu ra này và ghi vào file .json hay gửi như phản hồi API. Dùng json.dumps(indent=4) cho bất kỳ đầu ra nào máy cần đọc được.

simplejson — Thay thế Tương thích với stdlib

simplejson là thư viện đã trở thành module json tiêu chuẩn của Python — nó vẫn được duy trì độc lập và đi trước stdlib về các tính năng nhỏ. Đây là thay thế thực sự drop-in: đổi import và phần còn lại của code không thay đổi. Hữu ích khi bạn cần hỗ trợ Decimal không cần encoder tùy chỉnh, hoặc khi nhắm đến môi trường Python cũ hơn.

bash — cài đặt
pip install simplejson
Python 3.8+
import simplejson as json  # API giống hệt stdlib
from decimal import Decimal

don_hang = {
    "item": "Gói đăng ký API",
    "gia": Decimal("499000"),   # stdlib json raise TypeError ở đây
    "so_luong": 3,
}

# simplejson serialize Decimal gốc — không cần default=
print(json.dumps(don_hang, indent=4, use_decimal=True, ensure_ascii=False))
# {
#     "item": "Gói đăng ký API",
#     "gia": 499000.0,
#     "so_luong": 3
# }
Lưu ý:Để có hiệu năng thuần túy, orjson là lựa chọn tốt hơn. Dùng simplejson khi bạn cần serialize Decimal gốc không cần viết encoder tùy chỉnh, hoặc khi duy trì codebase đã dùng nó.

Xử lý File JSON Lớn mà Không Hết Bộ nhớ

json.load() đọc toàn bộ file vào bộ nhớ trước khi bạn có thể truy cập một trường đơn. Trên file với hàng triệu bản ghi hoặc payload trên một gigabyte, điều đó gây ra MemoryError — hoặc tốt nhất là buộc tiến trình phải dùng swap và chạy rất chậm.

Streaming với ijson

ijson là bộ phân tích JSON streaming tạo ra các mục một lần từ đối tượng file. Bạn duyệt qua các phần tử mảng mà không bao giờ giữ toàn bộ dataset trong bộ nhớ — bộ nhớ đỉnh tỷ lệ với một đối tượng đơn, không phải kích thước file.

bash — cài đặt
pip install ijson
Python 3.8+
import ijson
from decimal import Decimal

# su_kien.json có cấu trúc: {"su_kien": [...hàng triệu đối tượng...]}
tong_doanh_thu = Decimal("0")
so_luong_login = 0

with open("su_kien.json", "rb") as f:   # ijson yêu cầu chế độ nhị phân
    for su_kien in ijson.items(f, "su_kien.item"):
        if su_kien.get("loai") == "mua_hang":
            tong_doanh_thu += Decimal(str(su_kien["so_tien_vnd"]))
        elif su_kien.get("loai") == "login":
            so_luong_login += 1

print(f"Doanh thu: {tong_doanh_thu:,.0f} VND  |  Đăng nhập: {so_luong_login}")
# Xử lý file 2 GB với ~30 MB bộ nhớ đỉnh
Lưu ý:Chuyển từ json.load() sang ijson khi file của bạn vượt quá khoảng 50–100 MB. Dưới ngưỡng đó json.load() đơn giản hơn và nhanh hơn đáng kể vì dùng bộ phân tích C-extension nội bộ. Trên 100 MB, lợi ích tiết kiệm bộ nhớ từ streaming vượt trội so với chi phí thêm.

NDJSON — một đối tượng JSON mỗi dòng

NDJSON (Newline Delimited JSON, còn gọi là JSON Lines hoặc .jsonl) lưu một đối tượng JSON hoàn chỉnh mỗi dòng. Bộ xuất log, Kafka consumer và data pipeline thường tạo ra định dạng này vì mỗi dòng có thể được thêm vào và đọc độc lập — không cần parse toàn bộ file để thêm bản ghi. Thư viện chuẩn xử lý nó không cần phụ thuộc thêm.

Python 3.8+
import json
from pathlib import Path

# Ghi NDJSON — một sự kiện mỗi dòng
su_kien_list = [
    {"ts": "2024-05-01T10:00:00Z", "user": "usr_9f3a2b", "action": "login"},
    {"ts": "2024-05-01T10:01:03Z", "user": "usr_9f3a2b", "action": "purchase", "sku": "goi-pro"},
    {"ts": "2024-05-01T10:15:42Z", "user": "usr_4ab1d9", "action": "login"},
]

with open("su_kien.ndjson", "w", encoding="utf-8") as f:
    for sk in su_kien_list:
        f.write(json.dumps(sk, ensure_ascii=False) + "\n")

# Đọc NDJSON — bộ nhớ cố định, dù file lớn đến đâu
so_mua_hang = 0
with open("su_kien.ndjson", "r", encoding="utf-8") as f:
    for dong in f:
        dong = dong.strip()
        if not dong:           # bỏ qua dòng trống
            continue
        sk = json.loads(dong)
        if sk.get("action") == "purchase":
            so_mua_hang += 1
            print(f"{sk['ts']} — {sk['user']} mua {sk['sku']}")

Lỗi Phổ biến

Tôi đã thấy bốn lỗi này trong hầu hết mọi buổi code review liên quan đến serialization JSON — đặc biệt từ các developer đến từ JavaScript nơi JSON.stringify xử lý encoding tự động.

Dùng print(data) thay vì json.dumps()

Vấn đề: print() trên dict dùng Python repr — đầu ra hiển thị True/None (cú pháp Python), không phải true/null (cú pháp JSON). Không phải JSON hợp lệ.

Giải pháp: Luôn dùng json.dumps(data, indent=4) để có đầu ra JSON hợp lệ, dễ đọc.

Before · Python
After · Python
data = {"hoat_dong": True, "dem": None}
print(data)
# {'hoat_dong': True, 'dem': None}
print(json.dumps(data, indent=4))
# {
#     "hoat_dong": true,
#     "dem": null
# }
Quên ensure_ascii=False với văn bản tiếng Việt

Vấn đề: Ký tự đặc biệt (chữ có dấu, emoji) bị escape thành chuỗi \\uXXXX, làm đầu ra khó đọc.

Giải pháp: Truyền ensure_ascii=False để giữ nguyên ký tự Unicode gốc.

Before · Python
After · Python
nguoi_dung = {"ten": "Nguyễn Văn An", "thanh_pho": "Hà Nội"}
json.dumps(nguoi_dung, indent=2)
# {"ten": "Nguy\u1ec5n V\u0103n An", "thanh_pho": "H\u00e0 N\u1ed9i"}
json.dumps(nguoi_dung, indent=2, ensure_ascii=False)
# {"ten": "Nguyễn Văn An", "thanh_pho": "Hà Nội"}
Dùng json.dumps() để ghi vào file

Vấn đề: json.dumps() trả về string; bạn cần thêm lời gọi f.write(), tạo chuỗi trung gian không cần thiết.

Giải pháp: Dùng json.dump(data, f, indent=4) — ghi trực tiếp vào đối tượng file.

Before · Python
After · Python
with open("output.json", "w") as f:
    f.write(json.dumps(data, indent=4))
with open("output.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=4, ensure_ascii=False)
Dùng pprint và mong đợi JSON hợp lệ

Vấn đề: pprint.pprint() dùng cú pháp Python (True, None, dấu nháy đơn) mà bộ phân tích JSON từ chối.

Giải pháp: Dùng json.dumps(indent=4) cho bất kỳ đầu ra nào phải có thể parse như JSON.

Before · Python
After · Python
import pprint
pprint.pprint({"dang_chay": True, "loi_cuoi": None})
# {'dang_chay': True, 'loi_cuoi': None}
import json
print(json.dumps({"dang_chay": True, "loi_cuoi": None}, indent=4))
# {"dang_chay": true, "loi_cuoi": null}

So sánh Phương thức — json.dumps, orjson, simplejson, rich

Dùng json.dumps() cho định dạng hằng ngày và ghi file — nó bao gồm 95% trường hợp dùng với không phụ thuộc. Chuyển sang orjson khi serialize trong hot path hoặc khi đối tượng bao gồm trường datetime UUID. Dùng simplejson khi cần tương thích drop-in với stdlib cùng hỗ trợ Decimal sẵn sàng dùng. Giữ rich.print_json() pprint chặt chẽ cho kiểm tra terminal local — không cái nào tạo ra đầu ra mà máy đọc được.

Phương thức
Đầu ra
JSON hợp lệ
Tốc độ
Non-ASCII
Kiểu tùy chỉnh
Cài đặt
json.dumps(indent=4)
String
Tiêu chuẩn
ensure_ascii=False
default= / JSONEncoder
Tích hợp sẵn
json.dump(f, indent=4)
File
Tiêu chuẩn
ensure_ascii=False
default= / JSONEncoder
Tích hợp sẵn
pprint.pprint()
Python repr
Tiêu chuẩn
Gốc
✅ (bất kỳ repr)
Tích hợp sẵn
orjson.dumps(OPT_INDENT_2)
Bytes
Nhanh hơn 5–10×
Gốc
datetime, UUID, numpy
pip install orjson
python -m json.tool
stdout CLI
Tiêu chuẩn
Tích hợp sẵn
simplejson.dumps()
String
Nhanh hơn ~1.5×
ensure_ascii=False
Decimal gốc
pip install simplejson
rich.print_json()
Chỉ terminal
✅ (đầu vào)
Tiêu chuẩn
pip install rich

Câu hỏi Thường gặp

Làm thế nào để in đẹp JSON trong Python?

Gọi json.dumps(data, indent=4). Tham số indent xác định số khoảng trắng mỗi cấp lồng nhau. Import module json trước — nó đi kèm với thư viện chuẩn của Python nên không cần pip install. Truyền ensure_ascii=False nếu dữ liệu chứa ký tự non-ASCII như chữ có dấu hoặc ký tự CJK.

python
import json

nguoi_dung = {"username": "nguyenvana", "plan": "enterprise", "quyen": ["doc", "ghi", "deploy"]}
print(json.dumps(nguoi_dung, indent=4, ensure_ascii=False))

Sự khác biệt giữa json.dumps() và json.dump() là gì?

json.dumps() (có chữ "s") trả về chuỗi được định dạng trong bộ nhớ. json.dump() (không có "s") ghi trực tiếp vào đối tượng file — truyền file handle đã mở làm đối số thứ hai. Để ghi JSON được định dạng ra đĩa, json.dump(data, f, indent=4) là cách thông dụng và tránh tạo chuỗi trung gian.

python
# dumps → chuỗi trong bộ nhớ
da_dinh_dang = json.dumps(data, indent=4)

# dump → ghi trực tiếp vào file
with open('output.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, indent=4)

Tại sao json.dumps() hiển thị \u1ed3\u1ea3 thay vì ký tự thực?

Mặc định ensure_ascii=True escape mọi ký tự non-ASCII thành chuỗi \uXXXX. Đặt ensure_ascii=False để giữ nguyên ký tự Unicode gốc. Điều này đặc biệt quan trọng với tên người, địa chỉ và mọi nội dung do người dùng tạo bằng tiếng Việt hoặc các ngôn ngữ không dùng Latin.

python
data = {"thanh_pho": "Hà Nội", "chao": "Xin chào", "dat_nuoc": "Việt Nam"}

# Mặc định — bị escape
json.dumps(data, indent=4)
# {"thanh_pho": "H\u00e0 N\u1ed9i", ...}

# Đọc được
json.dumps(data, indent=4, ensure_ascii=False)
# {"thanh_pho": "Hà Nội", "chao": "Xin chào", "dat_nuoc": "Việt Nam"}

Làm thế nào để in đẹp một chuỗi JSON (không phải dict)?

Đầu tiên parse chuỗi với json.loads(), sau đó định dạng với json.dumps(). Hai lời gọi có thể được nối trong một dòng để kiểm tra nhanh trên terminal.

python
import json

raw = '{"endpoint":"/api/v2/nguoi-dung","timeout":30,"thu_lai":true}'
print(json.dumps(json.loads(raw), indent=4))

Tôi có thể dùng pprint để định dạng JSON trong Python không?

pprint.pprint() tạo ra đại diện đối tượng Python, không phải JSON hợp lệ. Nó dùng True/False/None (cú pháp Python) thay vì true/false/null (cú pháp JSON). Đừng bao giờ truyền đầu ra của pprint cho API hay bộ phân tích JSON — dùng json.dumps(indent=4) cho bất cứ thứ gì phải là JSON hợp lệ.

python
import pprint, json

data = {"hoat_dong": True, "diem": None}

pprint.pprint(data)     # {'hoat_dong': True, 'diem': None}  ← không phải JSON
json.dumps(data, indent=4)  # {"hoat_dong": true, "diem": null}  ← JSON hợp lệ

Làm thế nào để sắp xếp khóa JSON theo thứ tự chữ cái trong Python?

Thêm sort_keys=True vào json.dumps(). Trên dòng lệnh, dùng python -m json.tool --sort-keys data.json. Khóa được sắp xếp giúp diff JSON dễ đọc và phát hiện các giá trị thay đổi nhanh hơn.

python
import json

may_chu = {"workers": 4, "host": "0.0.0.0", "port": 8080, "debug": False}
print(json.dumps(may_chu, indent=4, sort_keys=True))
# {
#     "debug": false,
#     "host": "0.0.0.0",
#     "port": 8080,
#     "workers": 4
# }

Python cho bạn toàn quyền kiểm soát — serializer tùy chỉnh, streaming, tích hợp pipeline. Khi chỉ cần kiểm tra hoặc chia sẻ một đoạn code đã được định dạng, JSON Formatter của ToolDeck là con đường nhanh hơn: dán JSON vào và nhận kết quả có thụt lề và tô sáng mà không cần thiết lập môi trường.

Công cụ liên quan

Cũng có sẵn trong:GoJavaScriptBash
MS
Maria SantosBackend Developer

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.

DV
Dmitri VolkovNgười đánh giá kỹ thuật

Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.