Giả sử chúng ta có một đối tượng phức tạp và chúng ta muốn chuyển đổi nó thành một chuỗi, để gửi nó qua mạng hoặc chỉ để xuất nó cho mục đích ghi nhật ký.
Đương nhiên, một chuỗi như vậy nên bao gồm tất cả các thuộc tính quan trọng.
Chúng ta có thể thực hiện chuyển đổi như thế này:
let user = {
name: "David",
age: 30,
toString() {
return `{name: "${this.name}", age: ${this.age}}`;
}
};
alert(user); // {name: "David", age: 30}
Tuy nhiên, trong quá trình phát triển, các thuộc tính mới được thêm vào, các thuộc tính cũ được đổi tên và loại bỏ. Cập nhật như vậy toString
mỗi lần có thể trở thành một nỗi đau. Chúng ta có thể thử lặp lại các thuộc tính trong nó, nhưng nếu đối tượng phức tạp và có các đối tượng lồng nhau trong các thuộc tính thì sao? Chúng ta cũng cần phải thực hiện chuyển đổi của nó.
May mắn thay, không cần phải viết code để xử lý tất cả điều này. Nhiệm vụ đã được giải quyết với cách sau.
Nội dung chính
1. JSON.stringify
Các JSON (JavaScript Object Notation) là một định dạng chung để đại diện cho các giá trị và các đối tượng. Nó được mô tả như trong tiêu chuẩn RFC 4627 . Ban đầu nó được tạo ra cho JavaScript, nhưng nhiều ngôn ngữ khác cũng có thư viện để xử lý nó. Vì vậy, thật dễ dàng để sử dụng JSON để trao đổi dữ liệu khi máy khách sử dụng JavaScript và máy chủ được viết trên Ruby / PHP / Java / Sao cũng được.
JavaScript cung cấp các phương thức:
JSON.stringify
để chuyển đổi các đối tượng thành JSON.JSON.parse
để chuyển đổi JSON trở lại thành một đối tượng.
Ví dụ, ở đây chúng ta JSON.stringify
một sinh viên:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
let student = {
name: 'David',
age: 30,
isAdmin: false,
courses: ['html', 'css', 'js'],
wife: null
};
let json = JSON.stringify(student);
alert(typeof json); // we've got a string!
alert(json);
/* JSON-encoded object:
{
"name": "David",
"age": 30,
"isAdmin": false,
"courses": ["html", "css", "js"],
"wife": null
}
*/
Phương thức JSON.stringify(student)
lấy đối tượng và chuyển đổi nó thành một chuỗi.
Kết quả là chuỗijson
được gọi là JSON-encoded hoặc sự sắp đặt hoặc chuyển đổi thành chuỗi hoặc đối tượng marshalled. Chúng ta đã sẵn sàng để gửi nó qua hoặc đưa vào một kho lưu trữ dữ liệu đơn giản.
Xin lưu ý rằng một đối tượng được mã hóa JSON có một số khác biệt quan trọng so với nghĩa đen của đối tượng:
- Chuỗi sử dụng dấu ngoặc kép. Không có dấu ngoặc đơn hoặc backticks trong JSON. Vì vậy,
'John'
trở thành"John"
. - Tên thuộc tính đối tượng cũng được trích dẫn kép. Đó là điều bắt buộc. Vì vậy,
age:30
trở thành"age":30
.
JSON.stringify
có thể được áp dụng tốt cho kiểu nguyên thủy.
JSON hỗ trợ các kiểu dữ liệu sau:
- Các đối tượng
{ ... }
- Mảng
[ ... ]
- Kiểu Nguyên thủy:
- string,
- số,
- giá trị boolean
true/false
, null
.
Ví dụ:
// a number in JSON is just a number
alert( JSON.stringify(1) ) // 1
// a string in JSON is still a string, but double-quoted
alert( JSON.stringify('test') ) // "test"
alert( JSON.stringify(true) ); // true
alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]
JSON là đặc tả độc lập với ngôn ngữ dữ liệu, do đó một số thuộc tính đối tượng dành riêng cho JavaScript bị bỏ qua JSON.stringify
.
Cụ thể là:
- Thuộc tính hàm (phương thức).
- Thuộc tính ký hiệu(Biểu tượng).
- Thuộc tính mà lưu trữ
undefined
.
let user = {
sayHi() { // ignored
alert("Hello");
},
[Symbol("id")]: 123, // ignored
something: undefined // ignored
};
alert( JSON.stringify(user) ); // {} (empty object)
Thường thì không sao. Nếu đó không phải là những gì chúng ta muốn.
Điều tuyệt vời là các đối tượng lồng nhau được hỗ trợ và chuyển đổi tự động.
Ví dụ:
let meetup = {
title: "Conference",
room: {
number: 23,
participants: ["john", "ann"]
}
};
alert( JSON.stringify(meetup) );
/* The whole structure is stringified:
{
"title":"Conference",
"room":{"number":23,"participants":["john","ann"]},
}
*/
Quan trọng: Muốn tạo ra JSON thì object không được có tham chiếu vòng tròn.
Ví dụ:
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: ["john", "ann"]
};
meetup.place = room; // meetup references room
room.occupiedBy = meetup; // room references meetup
JSON.stringify(meetup); // Error: Converting circular structure to JSON
Ở đây, việc chuyển đổi không thành công, vì có tham chiếu vòng tròn: room.occupiedBy
tham chiếu meetup
và meetup.place
tham chiếu room
:
2. Không bao gồm khi chuyển đổi: replacer
Cú pháp đầy đủ JSON.stringify
là:
let json = JSON.stringify(value[, replacer, space])
value
Một giá trị để mã hóa.
replacer
Mảng các thuộc tính để mã hóa hoặc hàm ánh xạ function(key, value)
.
space
Lượng không gian sử dụng để định dạng
Hầu hết thời gian, JSON.stringify
chỉ được sử dụng với đối số đầu tiên. Nhưng nếu chúng ta cần tinh chỉnh quá trình thay thế, như lọc ra các tham chiếu vòng tròn, chúng ta có thể sử dụng đối số thứ hai JSON.stringify
.
Nếu chúng ta truyền một mảng các thuộc tính cho nó, chỉ các thuộc tính này sẽ được mã hóa.
Ví dụ:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
alert( JSON.stringify(meetup, ['title', 'participants']) );
// {"title":"Conference","participants":[{},{}]}
Ở đây áp dụng quy tắc nghiêm ngặt. Danh sách thuộc tính được áp dụng cho toàn bộ cấu trúc đối tượng. Vì vậy, các đối tượng trong participants
là trống rỗng, vì name
không có trong danh sách.
Bao gồm trong danh sách mọi thuộc tính ngoại trừ room.occupiedBy
điều đó sẽ gây ra tham chiếu vòng tròn:
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
alert( JSON.stringify(meetup, ['title', 'participants', 'place', 'name', 'number']) );
/*
{
"title":"Conference",
"participants":[{"name":"John"},{"name":"Alice"}],
"place":{"number":23}
}
*/
Bây giờ tất cả mọi thứ ngoại trừ occupiedBy
được chuyển đổi nối tiếp. Nhưng danh sách thuộc tính khá dài.
May mắn thay, chúng ta có thể sử dụng một hàm thay vì một mảng như replacer
.
Hàm này sẽ được gọi cho mỗi cặp (key, value)
và sẽ trả về giá trị của Thay thế xác định, được sử dụng thay cho giá trị ban đầu. Hoặc undefined
nếu giá trị được bỏ qua.
Trong trường hợp của chúng ta, chúng ta có thể trả lại tất cả mọi thứ value
trừ occupiedBy
. Để bỏ qua occupiedBy
, code dưới đây trả về undefined
:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
alert( JSON.stringify(meetup, function replacer(key, value) {
alert(`${key}: ${value}`);
return (key == 'occupiedBy') ? undefined : value;
}));
/* key:value pairs that come to replacer:
: [object Object]
title: Conference
participants: [object Object],[object Object]
0: [object Object]
name: John
1: [object Object]
name: Alice
place: [object Object]
number: 23
*/
Xin lưu ý rằng hàm replacer
nhận được mọi cặp khóa / giá trị bao gồm các đối tượng lồng nhau và các mục của mảng. Nó được áp dụng đệ quy. Giá trị this
bên trong replacer
là đối tượng chứa thuộc tính hiện tại.
Lệnh gọi đầu tiên khá đặc biệt. Nó được tạo ra bằng cách sử dụng một đối tượng trình bao bọc đặc biệt {"": meetup}
. Nói cách khác, cặp(key, value)
đầu tiên có một khóa trống và giá trị là toàn bộ đối tượng đích. Đó là lý do tại sao dòng đầu tiên là ":[object Object]"
trong ví dụ trên.
Ý tưởng là cung cấp càng nhiều cáchreplacer
càng tốt: nó có cơ hội phân tích và thay thế / bỏ qua toàn bộ đối tượng nếu cần thiết.
3. Định dạng: space
Đối số thứ ba JSON.stringify(value, replacer, space)
là số lượng khoảng trắng được sử dụng cho định dạng đẹp.
Trước đây, tất cả các đối tượng được xâu chuỗi không có vết lõm và khoảng trắng thừa. Điều đó tốt nếu chúng ta muốn gửi một đối tượng qua mạng. Đối sốspace
được sử dụng riêng cho một đầu ra đẹp.
Ở đây space = 2
bảo JavaScript hiển thị các đối tượng lồng nhau trên nhiều dòng, với thụt 2 khoảng trắng bên trong một đối tượng:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
let user = {
name: "John",
age: 25,
roles: {
isAdmin: false,
isEditor: true
}
};
alert(JSON.stringify(user, null, 2));
/* two-space indents:
{
"name": "John",
"age": 25,
"roles": {
"isAdmin": false,
"isEditor": true
}
}
*/
/* for JSON.stringify(user, null, 4) the result would be more indented:
{
"name": "John",
"age": 25,
"roles": {
"isAdmin": false,
"isEditor": true
}
}
*/
Các tham số space
được sử dụng chỉ duy nhất cho mục đích khai thác tạo log và làm đẹp-đầu ra.
4. Custom “toJSON”
Giống như chuyển đổi chuỗitoString
, một đối tượng có thể cung cấp phương thức toJSON
cho chuyển đổi sang JSON. JSON.stringify
tự động gọi nó nếu có sẵn.
Ví dụ:
let room = {
number: 23
};
let meetup = {
title: "Conference",
date: new Date(Date.UTC(2017, 0, 1)),
room
};
alert( JSON.stringify(meetup) );
/*
{
"title":"Conference",
"date":"2017-01-01T00:00:00.000Z", // (1)
"room": {"number":23} // (2)
}
*/
Ở đây chúng ta có thể thấy rằng date(1)
đã trở thành một chuỗi. Đó là bởi vì tất cả các ngày đều có một toJSON
phương thức tích hợp trả về kiểu chuỗi như vậy.
Bây giờ hãy thêm một tùy chỉnh toJSON
cho đối tượng của chúng ta room(2)
:
let room = {
number: 23,
toJSON() {
return this.number;
}
};
let meetup = {
title: "Conference",
room
};
alert( JSON.stringify(room) ); // 23
alert( JSON.stringify(meetup) );
/*
{
"title":"Conference",
"room": 23
}
*/
Như chúng ta có thể thấy, toJSON
được sử dụng cả cho lệnh gọi trực tiếp JSON.stringify(room)
và khi room
được lồng trong một đối tượng được mã hóa khác.
5. JSON.parse
Để giải mã chuỗi JSON, chúng ta cần một phương thức khác có tên JSON.parse .
Cú pháp:
let value = JSON.parse(str, [reviver]);
str
Chuỗi JSON để phân tích và chuyển đổi.
reviver
Hàm tùy chọn (khóa, giá trị) sẽ được gọi cho mỗi cặp(key, value)
và có thể chuyển đổi giá trị.
Ví dụ:
// stringified array
let numbers = "[0, 1, 2, 3]";
numbers = JSON.parse(numbers);
alert( numbers[1] ); // 1
Hoặc cho các đối tượng lồng nhau:
let userData = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';
let user = JSON.parse(userData);
alert( user.friends[1] ); // 1
JSON có thể phức tạp đến mức cần thiết, các đối tượng và mảng có thể bao gồm các đối tượng và mảng khác. Nhưng họ phải tuân theo cùng định dạng JSON.
Dưới đây là những lỗi điển hình trong JSON viết tay (đôi khi chúng ta phải viết nó cho mục đích gỡ lỗi):
let json = `{
name: "John", // mistake: property name without quotes
"surname": 'Smith', // mistake: single quotes in value (must be double)
'isAdmin': false // mistake: single quotes in key (must be double)
"birthday": new Date(2000, 2, 3), // mistake: no "new" is allowed, only bare values
"friends": [0,1,2,3] // here all fine
}`;
Bên cạnh đó, JSON không hỗ trợ comment. Thêm một comment vào JSON làm cho nó không hợp lệ.
Có một định dạng khác có tên JSON5, cho phép các khóa, comment không được trích dẫn, v.v. Nhưng đây là một thư viện độc lập, không có trong đặc điểm kỹ thuật của ngôn ngữ.
JSON thông thường nghiêm ngặt không phải vì các developer của nó lười biếng, mà cho phép thực hiện thuật toán phân tích cú pháp dễ dàng, đáng tin cậy và rất nhanh.
6. Sử dụng reviver
Hãy tưởng tượng, chúng ta đã có một đối tượngmeetup
chuỗi từ máy chủ.
Nó trông như thế này:
// title: (meetup title), date: (meetup date)
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
… Và bây giờ chúng ta cần phải deserialize nó, để tạo đối tượng JavaScript.
Hãy làm điều đó bằng cách gọi JSON.parse
:
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str);
alert( meetup.date.getDate() ); // Error!
Rất tiếc! Một lỗi!
Giá trị của meetup.date
là một chuỗi, không phải là một đối tượng Date
. Làm thế nào có thể JSON.parse
biết rằng nó nên chuyển đổi chuỗi đó thành một Date
?
Chúng ta hãy chuyển đến hàm JSON.parse
và dùng đối số thứ hai, trả về tất cả các giá trị, như là date
sẽ trở thành Date
:
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str, function(key, value) {
if (key == 'date') return new Date(value);
return value;
});
alert( meetup.date.getDate() ); // now works!
Nhân tiện, nó cũng hoạt động cho các đối tượng lồng nhau:
let schedule = `{
"meetups": [
{"title":"Conference","date":"2017-11-30T12:00:00.000Z"},
{"title":"Birthday","date":"2017-04-18T12:00:00.000Z"}
]
}`;
schedule = JSON.parse(schedule, function(key, value) {
if (key == 'date') return new Date(value);
return value;
});
alert( schedule.meetups[1].date.getDate() ); // works!
7. Tóm lược
- JSON là một định dạng dữ liệu chuẩn và thư viện độc lập riêng cho hầu hết các ngôn ngữ lập trình.
- JSON hỗ trợ các đối tượng đơn giản, mảng, chuỗi, số, booleans và
null
. - JavaScript cung cấp các phương thức hàm để tuần tự hóa thành JSON và JSON.parse để đọc từ JSON.
- Cả hai phương thức đều hỗ trợ các hàm để đọc / ghi thông minh.
- Nếu một đối tượng có
toJSON
, thì nó được gọi bởiJSON.stringify
.
Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.
Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:
Chào thân ái và quyết thắng!