Menu

JSON trong JavaScript: JSON.stringify, JSON.parse và lỗi hay gặp

Cách chuyển object JavaScript sang JSON và ngược lại: JSON.stringify, JSON.parse, hàm replacer và reviver, cùng những giá trị bị mất khi chuyển qua lại.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

JSON là văn bản, không phải object

JSON (JavaScript Object Notation) là một định dạng văn bản để trao đổi dữ liệu. Nó trông giống object literal của JavaScript, nhưng thực chất là một chuỗi: một dãy ký tự bạn có thể gửi qua mạng, lưu vào tệp hoặc dán vào tệp cấu hình.

// A JavaScript object - a live value in memory.
const user = { name: "Rosa", age: 30 };

// JSON - a string of text that represents the same data.
const json = '{"name":"Rosa","age":30}';

Hai thứ này dễ bị nhầm vì trông giống nhau. Cách nghĩ giúp bạn không nhầm: object sống trong chương trình của bạn; JSON là hình dạng của chúng khi rời khỏi chương trình. Bạn chuyển đổi giữa hai dạng bằng hai hàm: JSON.stringify (object → chuỗi) và JSON.parse (chuỗi → object).

JSON.stringify: từ object sang chuỗi

Chuyển bất kỳ giá trị JavaScript nào sang dạng biểu diễn JSON của nó:

Kết quả là một chuỗi một dòng, không có dấu cách: gọn nhẹ, lý tưởng để gửi qua mạng. Phép kiểm tra typeof xác nhận điều đó: string, không phải object.

Để dễ đọc khi gỡ lỗi, hãy truyền thêm hai đối số. Đối số thứ hai là replacer (sẽ nói ở phần sau); truyền null nghĩa là "lấy tất cả". Đối số thứ ba là độ thụt lề:

Đó là cách in đẹp chuẩn. Dùng 2 hoặc 4 dấu cách, khớp với kết quả của hầu hết các công cụ.

JSON.parse: từ chuỗi sang object

Chiều ngược lại: nhận một chuỗi JSON và lấy lại một giá trị JavaScript.

Sau khi parse, bạn đang làm việc với một object bình thường: truy cập bằng dấu chấm, bằng ngoặc vuông, dùng các phương thức mảng, đủ cả.

JSON.parse rất khó tính. Tất cả những dòng sau đều ném ra SyntaxError:

JSON.parse("{name: 'Rosa'}");      // unquoted key, single quotes
JSON.parse('{"name": "Rosa",}');   // trailing comma
JSON.parse("// a comment\n{}");    // comments aren't allowed
JSON.parse("");                    // empty string

Mỗi khi dữ liệu đến từ bên ngoài chương trình, như response của fetch, một tệp hay dữ liệu người dùng nhập, hãy bọc việc parse trong try/catch:

Những giá trị bị mất khi chuyển qua lại

JSON hỗ trợ sáu kiểu giá trị: chuỗi, số, boolean, null, mảng và object thường. Mọi thứ khác trong JavaScript đều bị loại bỏ, bị biến đổi hoặc gây lỗi.

Chuyện gì xảy ra:

  • Hàm và undefined bị lặng lẽ loại khỏi object. Trong mảng, chúng trở thành null, vì mảng trong JSON không được có chỗ trống.
  • Object Date được tuần tự hóa thành chuỗi ISO thông qua phương thức toJSON của nó. Parse lại sẽ cho bạn chuỗi, không phải Date.
  • BigInt ném ra TypeError. Số trong JSON không có kiểu tương đương.
  • Map, Set và tham chiếu vòng cũng không hoạt động nếu không xử lý thêm.

Cách khắc phục cho Date khi chuyển qua lại là hàm reviver của JSON.parse:

Reviver chạy cho mọi cặp khóa/giá trị và cho phép bạn biến đổi giá trị ngay khi đọc vào.

Replacer: lọc những gì được tuần tự hóa

Đối số thứ hai của JSON.stringify cho phép bạn kiểm soát những gì có trong kết quả. Truyền một mảng các khóa để chỉ giữ lại các khóa đó:

Hoặc truyền một hàm để xử lý theo logic tùy ý: bỏ trường, che giá trị, biến đổi ngay lúc chạy:

Trả về undefined sẽ xóa khóa đó. Trả về bất kỳ giá trị nào khác sẽ thay thế nó.

Tùy chỉnh bằng toJSON

Nếu một object có phương thức toJSON, JSON.stringify sẽ gọi nó và tuần tự hóa giá trị trả về thay cho object. Đây là cách Date tự quyết định định dạng của mình, và bạn cũng có thể dùng cơ chế này:

Rất phù hợp cho các lớp muốn có một dạng công khai nhất quán, bất kể ai tuần tự hóa chúng.

Sao chép sâu (mẹo cũ và cách tốt hơn)

Nhiều năm liền, JSON.parse(JSON.stringify(obj)) là cách viết một dòng để sao chép sâu (deep clone) một object thường:

Cách này chạy được, miễn là object chỉ chứa các giá trị an toàn với JSON. Date, Map và hàm đều làm nó hỏng (xem các vấn đề khi chuyển qua lại ở trên).

JavaScript hiện đại có structuredClone, xử lý được Date, Map, Set, typed array và tham chiếu vòng:

Hãy dùng structuredClone khi có thể. Giữ mẹo JSON.parse(JSON.stringify(...)) để dùng khi cần sao chép nhanh dữ liệu thường.

Một ví dụ thực tế: fetch và parse

Nơi bạn đụng đến JSON nhiều nhất là HTTP. fetch không tự parse JSON cho bạn: bạn gọi .json() trên response (thực chất là JSON.parse trên phần thân của response):

Gửi JSON thì ngược lại: JSON.stringify phần thân rồi đặt header Content-Type.

await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Rosa", age: 30 }),
});

Hai mẫu này đủ cho phần lớn công việc với JSON trong thực tế.

Tiếp theo: optional chaining

JSON sau khi parse thường có các trường không bắt buộc: một user.address.city có thể không tồn tại, một response.data.items có thể bị thiếu. Truy cập thuộc tính lồng sâu mà không làm chương trình sập chính là việc của optional chaining (?.), nội dung của trang tiếp theo.

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

JSON và object JavaScript khác nhau thế nào?

JSON là một định dạng văn bản: một chuỗi trông giống object literal của JavaScript nhưng có quy tắc chặt chẽ hơn. Khóa phải nằm trong dấu nháy kép, chuỗi phải dùng dấu nháy kép, và giá trị chỉ giới hạn ở chuỗi, số, boolean, null, mảng và object thường. Một object JavaScript là giá trị đang sống trong bộ nhớ, có thể chứa hàm, undefined, instance của Date và mọi thứ khác.

Làm sao chuyển một object JavaScript sang JSON?

Gọi JSON.stringify(obj). Hàm này duyệt qua object và trả về một chuỗi JSON. Truyền thêm đối số thứ ba, ví dụ JSON.stringify(obj, null, 2), để in đẹp với thụt lề 2 dấu cách. Các hàm và giá trị undefined bị loại khỏi object và trở thành null khi nằm trong mảng.

Vì sao JSON.parse báo lỗi?

JSON.parse rất khắt khe: dấu phẩy thừa ở cuối, dấu nháy đơn, khóa không có dấu nháy và chú thích đều gây ra SyntaxError. Hãy bọc lời gọi trong try/catch mỗi khi dữ liệu đến từ một request mạng, một tệp hoặc người dùng, tức bất cứ nơi nào chuỗi có thể không phải JSON hợp lệ.

JSON.stringify có giữ nguyên Date không?

Không. Một Date trở thành chuỗi ISO như "2026-01-15T10:30:00.000Z". Khi bạn JSON.parse lại, bạn nhận được chuỗi đó chứ không phải object Date. Hãy dùng đối số reviver của JSON.parse để chuyển chuỗi ISO trở lại thành instance Date nếu bạn cần.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu