Lập trình · 21/10/2026

JSON.parse reviver trong JavaScript: quên return có thể làm mất dữ liệu

JSON hợp lệ nhưng JSON.parse trả về undefined sau khi thêm callback để kiểm tra dữ liệu. Tham số reviver là một bước biến đổi, không phải callback chỉ để quan sát. Giá trị trả về của nó quyết định giữ, thay thế hay xóa từng phần của kết quả.

JSON.parse reviver trong JavaScript: quên return có thể làm mất dữ liệu

JSON hợp lệ nhưng JSON.parse trả về undefined sau khi thêm callback để kiểm tra dữ liệu. Tham số reviver là một bước biến đổi, không phải callback chỉ để quan sát. Giá trị trả về của nó quyết định giữ, thay thế hay xóa từng phần của kết quả.

1. Callback không return có thể xóa cả kết quả

const visited = [];
const result = JSON.parse('{"count":2}', (key, value) => {
  visited.push(key);
});
console.log(result === undefined); // true
console.log(JSON.stringify(visited)); // ["count",""]

Callback trả undefined khi không có return. Reviver xử lý thuộc tính count rồi tới giá trị gốc với khóa rỗng. Vì callback cũng trả undefined ở lượt gốc, toàn bộ kết quả parse trở thành undefined. Đây không phải SyntaxError của JSON.

2. Trả lại value cho những trường cần giữ

const result = JSON.parse('{"label":"ready","enabled":false}', (key, value) => {
  if (key === 'label' && typeof value === 'string') {
    return value.toUpperCase();
  }
  return value;
});
console.log(JSON.stringify(result)); // {"label":"READY","enabled":false}

Nhánh mặc định return value giữ nguyên những phần không cần biến đổi, gồm cả object gốc. Không dùng return value || undefined để “bỏ giá trị trống”: cách đó còn xóa false, 0 và chuỗi rỗng dù chúng có thể hợp lệ.

Quy tắc theo tên label sẽ áp dụng ở mọi tầng có cùng tên. Nếu chỉ một đường dẫn cụ thể được phép thay đổi, cân nhắc parse trước rồi biến đổi theo schema rõ ràng thay vì lọc tên trường toàn cục.

3. Xóa phần tử mảng để lại ô trống

const result = JSON.parse('[1,2,3]', (key, value) => {
  if (value === 2) return undefined;
  return value;
});
console.log(result.length); // 3
console.log(Object.hasOwn(result, 1)); // false
console.log(JSON.stringify(result)); // [1,null,3]

Ví dụ tự kiểm chứng rằng mảng vẫn dài 3 và vị trí 1 không còn thuộc tính riêng. Khi xuất lại JSON, ô trống được biểu diễn thành null. Điều này không có nghĩa reviver đã thay số 2 bằng null trong bộ nhớ.

Nếu mục tiêu là danh sách ngắn hơn, parse rồi dùng bước lọc có chủ đích. Cần chọn rõ loại bỏ theo giá trị, theo chỉ mục hay theo thuộc tính bản ghi; không dùng việc trả undefined như một phép dồn mảng.

4. Con được xử lý trước cha

const order = [];
const result = JSON.parse('{"child":{"value":2}}', (key, value) => {
  order.push(key);
  if (key === 'value') return value * 10;
  if (key === 'child') console.log(value.value); // 20
  return value;
});
console.log(JSON.stringify(order)); // ["value","child",""]
console.log(result.child.value); // 20

Reviver ở child nhận object đã chứa giá trị biến đổi của con. Lượt cuối xử lý gốc dùng khóa ''. Tuy nhiên, JSON cũng có thể chứa một thuộc tính thật với tên rỗng, nên chỉ dựa vào key === '' không phải cách nhận diện gốc an toàn cho mọi dữ liệu tùy ý.

JSON.parse không tự xác nhận schema ứng dụng. Sau biến đổi, vẫn cần kiểm tra các trường bắt buộc, kiểu và miền giá trị. Các ví dụ trong bài chỉ dùng JSON nhỏ, không xử lý ngày tháng hoặc định dạng số đặc biệt.

5. Checklist khi thêm reviver

  • Mỗi nhánh cần giữ dữ liệu phải trả value.
  • Kiểm tra giá trị gốc trả về, không chỉ thuộc tính con.
  • Thử false, 0, chuỗi rỗng và null.
  • Thử mảng và kiểm tra cả length lẫn ô trống.
  • Xác định quy tắc có áp dụng cho trường cùng tên ở tầng khác không.
  • Kiểm tra schema sau biến đổi.

Tám khối ví dụ Việt–Anh đã chạy bằng Node.js, kèm kiểm tra giữ giá trị falsy và lượt xử lý gốc. Chưa kiểm thử tích hợp API hoặc hiệu năng. Tham khảo MDN JSON.parse và đặc tả ECMAScript JSON.parse.

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.