JSON Formatter JavaScript — JSON.stringify()
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 debug các phản hồi API trong Node.js, thứ đầu tiên làm tôi chậm lại là một bức tường JSON được nén — một lần gọi JSON.stringify(data, null, 2) là cấu trúc trở nên dễ đọc ngay lập tức. Để định dạng JSON trong JavaScript, bạn không cần gì ngoài runtime: JSON.stringify đã được tích hợp trong mọi trình duyệt và môi trường Node.js, không cần cài đặt. Nếu bạn chỉ cần kết quả nhanh mà không viết code, Trình định dạng JSON của ToolDeck thực hiện điều đó ngay lập tức. Hướng dẫn này bao gồm mọi thứ thực tế: tham số space, mảng và hàm replacer, xử lý Date, BigInt và tham chiếu vòng, đọc và ghi file JSON trong Node.js (18+), in đẹp từ CLI, và thư viện fast-json-stringify cho serialization trong môi trường production.
- ✓JSON.stringify(data, null, 2) đã tích hợp trong mọi trình duyệt và Node.js — không cần cài đặt.
- ✓Tham số replacer nhận một mảng (danh sách trắng key) hoặc hàm (biến đổi giá trị) — dùng để che giấu các trường nhạy cảm.
- ✓Đối tượng Date serialize tự động qua toJSON() → chuỗi ISO 8601; BigInt ném TypeError và cần replacer tùy chỉnh.
- ✓Tham chiếu vòng ném TypeError — sửa bằng seen Set trong hàm replacer, hoặc dùng thư viện flatted.
- ✓Để in đẹp từ CLI dùng node -p "JSON.stringify(require('./file.json'),null,2)" — không cần công cụ thêm.
Định Dạng JSON là Gì?
Định dạng JSON (còn gọi là in đẹp - pretty-printing) chuyển đổi một chuỗi JSON nén thành bố cục dễ đọc cho con người với thụt lề nhất quán và ngắt dòng. Dữ liệu bên trong giống hệt nhau — chỉ khoảng trắng thay đổi. JSON nén tối ưu cho truyền mạng khi mỗi byte quan trọng; JSON định dạng tối ưu cho debug, review code và kiểm tra log. Hàm JSON.stringify() của JavaScript xử lý cả hai trong một lần gọi bằng cách bật tắt tham số space.
{"orderId":"ord_8f2a91bc","status":"shipped","items":[{"sku":"HDMI-4K-2M","qty":2,"unitPrice":12.99}],"total":25.98}{
"orderId": "ord_8f2a91bc",
"status": "shipped",
"items": [
{
"sku": "HDMI-4K-2M",
"qty": 2,
"unitPrice": 12.99
}
],
"total": 25.98
}JSON.stringify() — Định Dạng JSON Tích Hợp Sẵn
JSON.stringify() là hàm toàn cục trong mọi môi trường JavaScript — trình duyệt, Node.js, Deno, Bun — không cần import. Đối số thứ ba của nó, space, kiểm soát thụt lề: truyền một số cho số khoảng trắng đó mỗi cấp, hoặc chuỗi '\t' cho tab. Bỏ qua (hoặc truyền null) và bạn sẽ nhận được output gọn một dòng.
const serverConfig = {
host: "api.payments.internal",
port: 8443,
workers: 4,
tls: { enabled: true, cert: "/etc/ssl/certs/api.pem" },
rateLimit: { requestsPerMinute: 1000, burst: 50 }
}
console.log(JSON.stringify(serverConfig, null, 2))
// {
// "host": "api.payments.internal",
// "port": 8443,
// "workers": 4,
// "tls": {
// "enabled": true,
// "cert": "/etc/ssl/certs/api.pem"
// },
// "rateLimit": {
// "requestsPerMinute": 1000,
// "burst": 50
// }
// }Tham số space nhận một số (1–10 khoảng trắng) hoặc chuỗi. Truyền ký tự tab tạo ra output mà nhiều editor và công cụ diff ưa thích. Bạn cũng có thể kết hợp cả ba tham số — đây là mẫu thực tế tôi dùng khi ghi JSON định dạng vào file config:
const telemetryEvent = {
eventId: "evt_3c7f9a2b",
service: "checkout-api",
severity: "warn",
latencyMs: 342,
region: "eu-west-1",
tags: ["payment", "timeout", "retry"]
}
// Thụt lề 2 khoảng trắng (phổ biến nhất trong dự án JS)
JSON.stringify(telemetryEvent, null, 2)
// Thụt lề tab (ưa thích bởi một số linter và công cụ config)
JSON.stringify(telemetryEvent, null, '\t')
// Gọn — không có khoảng trắng (để truyền mạng)
JSON.stringify(telemetryEvent)
// {"eventId":"evt_3c7f9a2b","service":"checkout-api",...}undefined, function và Symbol bị bỏ qua im lặng khỏi output. Nếu giá trị thuộc tính là undefined, key đó sẽ không xuất hiện trong chuỗi đã serialize — đây là nguồn lỗi phổ biến khi log các đối tượng có trường tùy chọn.Hàm Replacer — Lọc và Biến Đổi Output
Đối số thứ hai của JSON.stringify() là replacer. Nó có hai dạng: một mảng đặt danh sách trắng các key cụ thể, hoặc một hàm được gọi cho mỗi cặp key/value và có thể lọc, biến đổi hoặc che giấu giá trị. Tôi dùng dạng mảng khi cần một tập con nhanh, và dạng hàm khi cần che giấu dữ liệu nhạy cảm trước khi log.
Array Replacer — Đặt Danh Sách Trắng cho Key Cụ Thể
const order = {
orderId: "ord_8f2a91bc",
customer: {
id: "usr_4421",
email: "nguyen.van.an@example.vn",
passwordHash: "bcrypt:$2b$12$XKzV..."
},
items: [{ sku: "HDMI-4K-2M", qty: 2, unitPrice: 12.99 }],
createdAt: "2026-03-10T14:22:00Z"
}
// Chỉ đưa các trường an toàn vào output log
const safeLog = JSON.stringify(order, ["orderId", "items", "createdAt"], 2)
// {
// "orderId": "ord_8f2a91bc",
// "items": [{ "sku": "HDMI-4K-2M", "qty": 2, "unitPrice": 12.99 }],
// "createdAt": "2026-03-10T14:22:00Z"
// }
// passwordHash và customer.email bị loại trừFunction Replacer — Biến Đổi Giá Trị
const auditRecord = {
requestId: "req_7d2e91",
user: { id: "usr_4421", email: "nguyen.van.an@example.vn", apiKey: "sk-live-eKx9..." },
action: "update_billing",
timestamp: new Date("2026-03-10T14:22:00Z"),
durationMs: 87
}
function safeReplacer(key, value) {
// Che giấu các trường trông như bí mật hoặc thông tin cá nhân
if (key === "apiKey") return "[ĐÃ ẨN]"
if (key === "email") return value.replace(/(?<=.{2}).+(?=@)/, "***")
return value
}
console.log(JSON.stringify(auditRecord, safeReplacer, 2))
// {
// "requestId": "req_7d2e91",
// "user": { "id": "usr_4421", "email": "ng.***@example.vn", "apiKey": "[ĐÃ ẨN]" },
// "action": "update_billing",
// "timestamp": "2026-03-10T14:22:00.000Z",
// "durationMs": 87
// }this được đặt thành đối tượng chứa key hiện tại. Lần gọi đầu tiên truyền chuỗi rỗng làm key và toàn bộ giá trị đang được serialize làm value — trả về không thay đổi để tiếp tục với serialize bình thường.Xử Lý Các Kiểu Không Thể Serialize
Không phải tất cả giá trị JavaScript đều ánh xạ gọn gàng sang JSON. Biết cách mỗi kiểu hoạt động giúp ngăn mất dữ liệu im lặng và lỗi runtime không mong đợi trong code production.
Date — Tự Động qua toJSON()
Đối tượng Date có phương thức toJSON() trả về chuỗi ISO 8601. JSON.stringify() gọi toJSON() tự động trước khi serialize, vì vậy không cần xử lý tùy chỉnh.
const webhook = {
eventType: "payment.succeeded",
occurredAt: new Date("2026-03-10T14:22:00Z"),
processedAt: new Date()
}
JSON.stringify(webhook, null, 2)
// {
// "eventType": "payment.succeeded",
// "occurredAt": "2026-03-10T14:22:00.000Z",
// "processedAt": "2026-03-10T14:22:01.347Z"
// }
// Bất kỳ đối tượng nào có phương thức toJSON() đều được xử lý tương tự:
const custom = { toJSON: () => "custom-value", hidden: 42 }
JSON.stringify(custom) // '"custom-value"'Class Tùy Chỉnh — Triển Khai toJSON()
Bất kỳ class nào cũng có thể triển khai phương thức toJSON() và JSON.stringify() sẽ tự động gọi nó trong quá trình serialize. Cách này sạch hơn so với replacer toàn cục cho các kiểu domain xuất hiện khắp codebase.
class Money {
constructor(amount, currency) {
this.amount = amount
this.currency = currency
}
toJSON() {
// Được JSON.stringify tự động gọi
return { amount: this.amount, currency: this.currency, formatted: `${this.currency} ${this.amount.toFixed(2)}` }
}
}
class OrderId {
constructor(id) { this.id = id }
toJSON() { return this.id } // Serialize thành chuỗi đơn giản
}
const invoice = {
invoiceId: new OrderId('inv_8f2a91bc'),
subtotal: new Money(199.00, 'VND'),
tax: new Money(15.92, 'VND'),
issuedAt: new Date('2026-03-10T14:22:00Z')
}
JSON.stringify(invoice, null, 2)
// {
// "invoiceId": "inv_8f2a91bc",
// "subtotal": { "amount": 199, "currency": "VND", "formatted": "VND 199.00" },
// "tax": { "amount": 15.92, "currency": "VND", "formatted": "VND 15.92" },
// "issuedAt": "2026-03-10T14:22:00.000Z"
// }toJSON() được ưu tiên hơn hàm replacer. Nếu cả hai tồn tại, toJSON() chạy trước — replacer nhận giá trị đã được chuyển đổi, không phải instance class gốc.BigInt — TypeError Không Có Replacer
// Cái này ném: TypeError: Do not know how to serialize a BigInt
// JSON.stringify({ sessionId: 9007199254741234n })
// Giải pháp: chuyển BigInt thành chuỗi trong replacer
function bigIntReplacer(_key, value) {
return typeof value === 'bigint' ? value.toString() : value
}
const metrics = {
requestCount: 9007199254741234n, // vượt quá Number.MAX_SAFE_INTEGER
service: "ingestion-worker",
region: "us-east-1"
}
JSON.stringify(metrics, bigIntReplacer, 2)
// {
// "requestCount": "9007199254741234",
// "service": "ingestion-worker",
// "region": "us-east-1"
// }Tham Chiếu Vòng — TypeError Không Có Seen Set
// Cái này ném: TypeError: Converting circular structure to JSON
// const node = { id: "n1" }; node.self = node; JSON.stringify(node)
// Giải pháp: theo dõi đối tượng đã thấy bằng WeakSet
function circularReplacer() {
const seen = new WeakSet()
return function (_key, value) {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]'
seen.add(value)
}
return value
}
}
const parent = { id: "node_parent", label: "gốc" }
const child = { id: "node_child", parent }
parent.child = child // vòng
JSON.stringify(parent, circularReplacer(), 2)
// {
// "id": "node_parent",
// "label": "gốc",
// "child": { "id": "node_child", "parent": "[Circular]" }
// }
// Thay thế: npm install flatted
// import { stringify } from 'flatted'
// stringify(parent) // xử lý ref vòng nativeTham Khảo Tham Số JSON.stringify()
Cả ba tham số đều được hỗ trợ đầy đủ trong mọi runtime JavaScript hiện đại. Mặc định tạo ra JSON gọn một dòng — truyền tham số tường minh để có output dễ đọc.
Định Dạng JSON từ File và Phản Hồi API
Trong Node.js (18+) bạn thường cần định dạng lại file config JSON hoặc in đẹp phản hồi API để debug. Cả hai mẫu đều dùng cách tiếp cận hai bước giống nhau: parse văn bản thô bằng JSON.parse(), sau đó serialize lại bằng JSON.stringify().
Đọc và Ghi Lại File Config JSON
import { readFileSync, writeFileSync } from 'fs'
try {
const raw = readFileSync('./config/database.json', 'utf8')
const config = JSON.parse(raw)
writeFileSync('./config/database.json', JSON.stringify(config, null, 2))
console.log('Config đã được định dạng lại thành công')
} catch (err) {
console.error('Định dạng lại config thất bại:', err.message)
// JSON.parse ném SyntaxError nếu file chứa JSON không hợp lệ
// readFileSync ném ENOENT nếu file không tồn tại
}Định Dạng Lại File Async (fs/promises)
import { readFile, writeFile } from 'fs/promises'
async function reformatJson(filePath) {
const raw = await readFile(filePath, 'utf8')
const parsed = JSON.parse(raw)
const formatted = JSON.stringify(parsed, null, 2)
await writeFile(filePath, formatted, 'utf8')
return { keys: Object.keys(parsed).length }
}
// Sử dụng
const { keys } = await reformatJson('./config/feature-flags.json')
console.log(`Đã định dạng lại ${keys} key cấp cao nhất`)In Đẹp JSON từ Phản Hồi fetch()
Khi xây dựng hoặc debug API client trong Node.js 18+ hoặc trình duyệt, in đẹp body phản hồi là cách nhanh nhất để hiểu server trả về gì. Mẫu chuẩn là response.json() (đối tượng đã parse) rồi JSON.stringify(). Nếu bạn cần chuỗi thô trước — ví dụ để tính hash hoặc log nguyên văn — dùng response.text() rồi JSON.parse().
// Mẫu 1: response.json() → in đẹp
async function debugEndpoint(url) {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` }
})
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`)
const data = await res.json()
console.log(JSON.stringify(data, null, 2))
}
await debugEndpoint('https://api.stripe.com/v1/charges?limit=3')// Mẫu 2: response.text() → parse → định dạng
// Hữu ích khi muốn log cả phản hồi thô VÀ in đẹp nó
async function inspectRawResponse(url) {
const res = await fetch(url)
const raw = await res.text()
console.log('Độ dài phản hồi thô:', raw.length)
try {
const parsed = JSON.parse(raw)
console.log('Đã định dạng:')
console.log(JSON.stringify(parsed, null, 2))
} catch {
console.error('Phản hồi không phải JSON hợp lệ:', raw.slice(0, 200))
}
}In Đẹp từ Dòng Lệnh
Node.js có đủ khả năng để in đẹp JSON từ terminal mà không cần công cụ thêm. Những lệnh one-liner này hữu ích trong script CI, pipeline triển khai và alias shell. Để sử dụng tương tác nhanh hơn, cài jq — tiêu chuẩn de-facto để xử lý JSON trên dòng lệnh.
# In đẹp package.json dùng node -p (print)
node -p "JSON.stringify(require('./package.json'), null, 2)"
# In đẹp từ stdin (tương thích pipe, hoạt động trong CI)
echo '{"port":3000,"env":"production"}' | node -e "
const d = require('fs').readFileSync(0, 'utf8')
console.log(JSON.stringify(JSON.parse(d), null, 2))
"
# In đẹp phản hồi API đã lưu vào file
cat api-response.json | node -e "
process.stdin.setEncoding('utf8')
let s = ''
process.stdin.on('data', c => s += c)
process.stdin.on('end', () => console.log(JSON.stringify(JSON.parse(s), null, 2)))
"# Dự phòng Python (cài sẵn trên macOS và hầu hết distro Linux)
cat api-response.json | python3 -m json.tool
# jq — nhanh nhất và nhiều tính năng nhất (brew install jq / apt install jq)
cat api-response.json | jq .
# jq — in đẹp và lọc trong một bước
cat api-response.json | jq '.data.users[] | {id, email}'"type": "module" trong package.json), require() không có trong các lệnh one-liner. Dùng --input-type=module và fs.readFileSync thay thế, hoặc chuyển sang node -e với đoạn CommonJS như minh họa ở trên.Nếu bạn hoàn toàn không ở trong terminal — dán response từ Postman hoặc file log — Trình định dạng JSON của ToolDeck cho phép bạn dán, định dạng và sao chép trong một bước với tô sáng cú pháp và xác thực tích hợp.
Thay Thế Hiệu Năng Cao — fast-json-stringify
fast-json-stringify tạo ra hàm serializer chuyên dụng từ JSON Schema. Vì nó biết trước hình dạng của dữ liệu, nó có thể bỏ qua kiểm tra kiểu và dùng nối chuỗi thay vì đệ quy xuống — các benchmark thường cho thấy cải thiện throughput 2–5× so với JSON.stringify() trên các payload lớn, lặp đi lặp lại. Tôi dùng nó trong các route API tần suất cao khi chi phí serialize xuất hiện trong trace của profiler.
npm install fast-json-stringify
import fastJson from 'fast-json-stringify'
const serializeTelemetryEvent = fastJson({
title: 'TelemetryEvent',
type: 'object',
properties: {
eventId: { type: 'string' },
service: { type: 'string' },
severity: { type: 'string', enum: ['info', 'warn', 'error'] },
latencyMs: { type: 'integer' },
timestamp: { type: 'string' },
region: { type: 'string' }
},
required: ['eventId', 'service', 'severity', 'latencyMs', 'timestamp']
})
const event = {
eventId: 'evt_3c7f9a2b',
service: 'checkout-api',
severity: 'warn',
latencyMs: 342,
timestamp: new Date().toISOString(),
region: 'eu-west-1'
}
const json = serializeTelemetryEvent(event)
// '{"eventId":"evt_3c7f9a2b","service":"checkout-api","severity":"warn",...}'fast-json-stringify được thiết kế cho serialization production của dữ liệu có cấu trúc — luôn tạo output gọn (không in đẹp). Để có output dễ đọc trong quá trình phát triển, dùng JSON.stringify(data, null, 2) như bình thường.Output Terminal với Tô Sáng Cú Pháp
Hàm util.inspect() tích hợp của Node.js tạo ra output có màu, dễ đọc được tối ưu hóa cho hiển thị terminal. Nó xử lý tham chiếu vòng và BigInt native, và render đệ quy các đối tượng lồng nhau đến bất kỳ độ sâu nào. Output không phải JSON hợp lệ — nó dùng cú pháp JavaScript (ví dụ render function và Symbol thay vì bỏ qua), khiến nó lý tưởng cho script debug Node.js nhưng không phù hợp cho phản hồi API hoặc output file.
import { inspect } from 'util'
const payload = {
requestId: "req_7d2e91",
user: { id: "usr_4421", roles: ["admin", "billing"] },
metadata: { ipAddress: "203.0.113.42", userAgent: "Mozilla/5.0" },
createdAt: new Date()
}
// depth: null → mở rộng tất cả cấp lồng nhau; colors: true → màu terminal ANSI
console.log(inspect(payload, { colors: true, depth: null }))util.inspect() cho JSON ghi vào file, gửi qua mạng hoặc lưu trong database. Output của nó không phải JSON hợp lệ và sẽ gây lỗi parse trong bất kỳ hệ thống downstream nào gọi JSON.parse() trên nó. Chỉ dùng cho debug terminal tương tác.Làm Việc với File JSON Lớn
JSON.parse() tải toàn bộ file vào bộ nhớ trước khi parse — ổn với payload nhỏ, nhưng không thực tế với file trên 50–100 MB như export database, dump log ứng dụng hoặc batch phân tích. Với những trường hợp này, Node.js Streams và thư viện stream-json cho phép xử lý từng bản ghi mà không làm vỡ heap.
Phân Tích Streaming với stream-json
npm install stream-json
import { pipeline } from 'stream/promises'
import { createReadStream } from 'fs'
import { parser } from 'stream-json'
import { streamArray } from 'stream-json/streamers/StreamArray.js'
// events.json = mảng hàng triệu đối tượng — không bao giờ tải hoàn toàn vào bộ nhớ
await pipeline(
createReadStream('./events.json'),
parser(),
streamArray(),
async function* (source) {
for await (const { value: event } of source) {
if (event.severity === 'error') {
console.log(JSON.stringify(event, null, 2))
}
}
}
)NDJSON / JSON Lines — Không Cần Phụ Thuộc Thêm
NDJSON (Newline Delimited JSON) lưu một đối tượng JSON mỗi dòng và phổ biến trong export Kafka, output BigQuery và pipeline log có cấu trúc. Module readline tích hợp của Node.js xử lý nó mà không cần package nào từ bên thứ ba.
import { createReadStream } from 'fs'
import { createInterface } from 'readline'
// Định dạng: một đối tượng JSON mỗi dòng (log, export Kafka, BigQuery)
const rl = createInterface({
input: createReadStream('./logs.ndjson'),
crlfDelay: Infinity
})
for await (const line of rl) {
if (!line.trim()) continue
const entry = JSON.parse(line)
if (entry.level === 'error') {
console.log(JSON.stringify(entry, null, 2))
}
}JSON.parse() sang streaming khi file JSON vượt quá 50–100 MB hoặc khi xử lý stream không giới hạn (Kafka, pipeline log). Với NDJSON / JSON Lines, dùng readline — không cần phụ thuộc thêm.Lỗi Phổ Biến
Bốn lỗi này xuất hiện liên tục trong code review và báo cáo lỗi production. Mỗi lỗi liên quan đến một hành vi tinh tế của JSON.stringify() dễ bỏ qua và khó debug sau thực tế.
Vấn đề: Các thuộc tính đối tượng có giá trị undefined bị bỏ qua hoàn toàn khỏi output JSON — không có cảnh báo hay lỗi. Điều này gây mất dữ liệu vô hình khi đối tượng có các trường tùy chọn.
Giải pháp: Dùng null cho các giá trị cố ý vắng mặt phải xuất hiện trong output đã serialize. Giữ undefined chỉ cho các trường nên được loại trừ khỏi JSON.
const userProfile = {
userId: "usr_4421",
displayName: "Nguyễn Văn An",
avatarUrl: undefined, // sẽ biến mất im lặng
bio: undefined // sẽ biến mất im lặng
}
JSON.stringify(userProfile, null, 2)
// { "userId": "usr_4421", "displayName": "Nguyễn Văn An" }
// avatarUrl và bio đã biến mất — không có cảnh báoconst userProfile = {
userId: "usr_4421",
displayName: "Nguyễn Văn An",
avatarUrl: null, // cố ý vắng mặt — xuất hiện trong output
bio: null // cố ý vắng mặt — xuất hiện trong output
}
JSON.stringify(userProfile, null, 2)
// {
// "userId": "usr_4421",
// "displayName": "Nguyễn Văn An",
// "avatarUrl": null,
// "bio": null
// }Vấn đề: Truyền giá trị BigInt vào JSON.stringify() ném TypeError tại runtime. Đây là crash cứng, không phải bỏ qua im lặng — sẽ xuất hiện trong production nếu có trường số vượt quá Number.MAX_SAFE_INTEGER.
Giải pháp: Dùng hàm replacer chuyển đổi giá trị BigInt thành chuỗi trước khi serialize. Ngoài ra, chuyển đổi BigInt thành chuỗi hoặc Number ở tầng dữ liệu trước khi truyền cho JSON.stringify.
const session = {
sessionId: 9007199254741234n, // literal BigInt
userId: "usr_4421",
startedAt: "2026-03-10T14:00:00Z"
}
JSON.stringify(session, null, 2)
// Uncaught TypeError: Do not know how to serialize a BigIntfunction bigIntReplacer(_key, value) {
return typeof value === 'bigint' ? value.toString() : value
}
const session = {
sessionId: 9007199254741234n,
userId: "usr_4421",
startedAt: "2026-03-10T14:00:00Z"
}
JSON.stringify(session, bigIntReplacer, 2)
// {
// "sessionId": "9007199254741234",
// "userId": "usr_4421",
// "startedAt": "2026-03-10T14:00:00Z"
// }Vấn đề: Các đối tượng tham chiếu chính mình — phổ biến trong cây DOM, danh sách liên kết và một số result set ORM — ném TypeError khi truyền cho JSON.stringify(). Lỗi chỉ xảy ra tại thời điểm serialize, thường rất xa nơi tham chiếu vòng được tạo ra.
Giải pháp: Dùng hàm replacer với WeakSet để phát hiện và thay thế tham chiếu vòng, hoặc cài thư viện flatted như một replacement drop-in xử lý cấu trúc vòng native.
const dept = { id: "dept_eng", name: "Kỹ thuật" }
const team = { id: "team_frontend", dept }
dept.teams = [team] // vòng: dept → team → dept
JSON.stringify(dept, null, 2)
// Uncaught TypeError: Converting circular structure to JSONimport { stringify } from 'flatted' // npm install flatted
const dept = { id: "dept_eng", name: "Kỹ thuật" }
const team = { id: "team_frontend", dept }
dept.teams = [team]
// flatted xử lý ref vòng — lưu ý: output ở định dạng flatted, không phải JSON chuẩn
stringify(dept)
// Hoặc dùng replacer dựa trên WeakSet nếu cần output JSON chuẩn:
function circularReplacer() {
const seen = new WeakSet()
return (_key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]'
seen.add(value)
}
return value
}
}
JSON.stringify(dept, circularReplacer(), 2)Vấn đề: Truyền giá trị space lớn hơn 10 cho JSON.stringify() không ném lỗi — giá trị bị giới hạn im lặng ở 10. Lập trình viên mong đợi 20 khoảng trắng mỗi mức thụt lề cho cấu trúc lồng sâu sẽ chỉ nhận được 10, dẫn đến định dạng bất ngờ trong file được tạo ra.
Giải pháp: Thụt lề tối đa là 10 khoảng trắng. Với cấu trúc sâu, ưu tiên thụt lề 2 khoảng trắng (quy ước phổ biến nhất trong dự án JavaScript) và dựa vào editor có thể gập lại để điều hướng.
const deepConfig = { server: { tls: { certs: { primary: "/etc/ssl/api.pem" } } } }
// Mong đợi thụt lề 20 khoảng trắng — nhưng space bị giới hạn ở 10
JSON.stringify(deepConfig, null, 20)
// Output giống hệt JSON.stringify(deepConfig, null, 10)
// Không có lỗi, không có cảnh báo — bị cắt ngắn im lặngconst deepConfig = { server: { tls: { certs: { primary: "/etc/ssl/api.pem" } } } }
// Dùng 2 (hầu hết dự án) hoặc 4 khoảng trắng — không bao giờ vượt 10
JSON.stringify(deepConfig, null, 2)
// {
// "server": {
// "tls": {
// "certs": {
// "primary": "/etc/ssl/api.pem"
// }
// }
// }
// }JSON.stringify so với Các Lựa Chọn Thay Thế — So Sánh Nhanh
Các tình huống khác nhau cần công cụ khác nhau. JSON.stringify với replacer bao gồm hầu hết các trường hợp production mà không cần phụ thuộc. util.inspect là lựa chọn phù hợp cho debug terminal nhanh khi cần output có màu và không cần JSON hợp lệ. fast-json-stringify đáng đầu tư ở các route thông lượng cao khi profiling cho thấy chi phí serialize; với mọi thứ khác thì overhead duy trì schema không đáng.
Câu Hỏi Thường Gặp
Làm thế nào để in đẹp JSON trong JavaScript?
Gọi JSON.stringify(data, null, 2) — đối số thứ ba kiểm soát thụt lề. Truyền 2 hoặc 4 cho khoảng trắng, hoặc "\t" cho tab. Không cần import hay cài đặt: JSON là đối tượng toàn cục trong mọi môi trường JavaScript, bao gồm trình duyệt và Node.js.
const config = { host: "api.payments.internal", port: 8443, tls: true }
console.log(JSON.stringify(config, null, 2))
// {
// "host": "api.payments.internal",
// "port": 8443,
// "tls": true
// }Tham số `space` trong JSON.stringify() làm gì?
Tham số space kiểm soát thụt lề trong output. Truyền một số (1–10) cho số khoảng trắng đó mỗi cấp, hoặc một chuỗi như "\t" để dùng ký tự tab. Các giá trị trên 10 bị giới hạn im lặng ở 10. Truyền null, 0 hoặc bỏ qua tham số sẽ tạo JSON gọn một dòng.
const data = { service: "payments", version: 3, active: true }
JSON.stringify(data, null, 2) // thụt lề 2 khoảng trắng
JSON.stringify(data, null, 4) // thụt lề 4 khoảng trắng
JSON.stringify(data, null, '\t') // thụt lề tab
JSON.stringify(data) // gọn: {"service":"payments","version":3,"active":true}Tại sao JSON.stringify() trả về undefined cho một số giá trị?
JSON.stringify âm thầm bỏ qua các thuộc tính đối tượng có giá trị là undefined, function hoặc Symbol — những kiểu này không có biểu diễn trong JSON. Nếu chính giá trị cấp cao nhất là undefined, hàm trả về undefined (không phải chuỗi "undefined"). Dùng null thay vì undefined cho các trường tùy chọn phải xuất hiện trong output.
const event = {
traceId: "tr_9a2f",
handler: () => {}, // function — bị bỏ qua
requestId: undefined, // undefined — bị bỏ qua
sessionId: Symbol("s"), // Symbol — bị bỏ qua
status: "ok"
}
JSON.stringify(event, null, 2)
// { "traceId": "tr_9a2f", "status": "ok" }Làm thế nào để xử lý đối tượng Date khi định dạng JSON?
Đối tượng Date có phương thức toJSON() tích hợp trả về chuỗi ISO 8601, vì vậy JSON.stringify xử lý chúng tự động. Bạn không cần replacer tùy chỉnh cho ngày tháng — giá trị được serialize sẽ là một chuỗi như "2026-03-10T14:22:00.000Z".
const order = {
orderId: "ord_8f2a91bc",
placedAt: new Date("2026-03-10T14:22:00Z"),
total: 42.98
}
JSON.stringify(order, null, 2)
// {
// "orderId": "ord_8f2a91bc",
// "placedAt": "2026-03-10T14:22:00.000Z",
// "total": 42.98
// }Làm thế nào để định dạng chuỗi JSON (không phải đối tượng) trong JavaScript?
Parse chuỗi trước bằng JSON.parse(), sau đó serialize lại bằng JSON.stringify(). Hai lần gọi có thể được xâu chuỗi trong một dòng để debug nhanh.
const raw = '{"endpoint":"/api/v2/users","timeout":30,"retry":true}'
const formatted = JSON.stringify(JSON.parse(raw), null, 2)
console.log(formatted)
// {
// "endpoint": "/api/v2/users",
// "timeout": 30,
// "retry": true
// }Có thể dùng JSON.stringify() trong trình duyệt không?
Có. JSON là global tích hợp trong mọi trình duyệt hiện đại từ IE8 — không cần thẻ script hay import. Mở bảng điều khiển DevTools và gọi JSON.stringify() trực tiếp. Nó hoạt động giống hệt phiên bản Node.js, với cùng chữ ký tham số và cùng giới hạn về BigInt và tham chiếu vòng.
// Hoạt động trên Chrome, Firefox, Safari, Edge — không cần import
const payload = { userId: "usr_7b3c", action: "checkout", cart: ["SKU-001", "SKU-002"] }
copy(JSON.stringify(payload, null, 2)) // copy() là helper của DevToolsJavaScript cho bạn toàn quyền kiểm soát — hàm replacer, toJSON() tùy chỉnh, xử lý file lớn trong Node.js. Khi bạn chỉ cần kiểm tra hoặc chia sẻ một đoạn code đã định dạng, Trình định dạng JSON của ToolDeck là con đường nhanh nhất: dán JSON của bạn và nhận kết quả được định dạng, tô sáng mà không cần bất kỳ thiết lập môi trường nào.
Công Cụ Liên Quan
Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.
Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.