JSON
JSON methods, toJSON
ลองพูดว่า เรามี Object ที่ซับซ้อน และเราต้องการที่จะแปลงเป็น String เพื่อที่จะส่งข้าม network หรือเพื่อที่จะแสดง output ออกไป
โดยปกติแล้ว String ควรมี properties อยู่
เราสามารถสร้างมันได้โดยสิ่งนี้
let user = {
name: "John",
age: 30,
toString() {
return `{name: "${this.name}", age: ${this.age}}`;
}
};
alert(user); // {name: "John", age: 30}
แต่ในการทำงานจริงแล้ว การเพิ่ม property, การเปลี่ยนชื่อ, การลบ ในการ update toString ทุกครั้ง เป็นสิ่งที่ไม่ควรทำ เราสามารถ Loop properties ได้ แต่ถ้า Object ของเรา มมีความซับซ้อนมากกว่านี้ เราก็ต้องแก้ไข function ของเราด้วย
โชคดี ที่เราไม่จำเป็นต้องเขียน code รับมือกับมันทั้งหมด
JSON.stringify
JSON (JavaScript Object Notation) เป็น format ในการแสดงถึง values และ key โดยแต่แรก มันถูกสร้างมาใช้กับ JavaScript แต่หลาย ๆ ภาษา ก็มี libraries เพื่อจะใช้งานกับมันเหมือนกัน ดังนั้นการใช้ JSON ในการเปลี่ยนข้อมูลระหว่าง client ที่ใช้ JavaScript และ server ที่เขียนโดยภาษาอื่น
JavaScript มี method ที่ใช้กับ JSON:
JSON.stringifyเปลี่ยนจาก Object เป็น JSONJSON.parseเปลี่ยนจาก JSON เป็น Object
ตัวอย่างเช่น การใช้ JSON.stringify
let student = {
name: 'John',
age: 30,
isAdmin: false,
courses: ['html', 'css', 'js'],
spouse: null
};
let json = JSON.stringify(student);
alert(typeof json); // we've got a string!
alert(json);
/* JSON-encoded object:
{
"name": "John",
"age": 30,
"isAdmin": false,
"courses": ["html", "css", "js"],
"spouse": null
}
*/
method JSON.stringify(student) ทำการเปลี่ยนจาก Object เป็น String
ผลลัพธ์ของ json string เรียกว่า JSON-encoded or serialized หรือ stringified หรือ marshalled object ดังนั้น เราพร้อมส่งข้อมูลในรูปแบบของ JSON แล้ว
จำไว้ว่า JSON-encoded object มีข้อแตกต่างกับ Object ธรรมดา หลายจุด
- String ต้องใช้ Double Quotes, ห้ามใช้ Single Quote หรือ Backticks ในรูปเเบบ JSON
- Property ต้องใช้ Double Quotes, เหมือนกัน เป็นข้อบังคับ
Method JSON.stringify ยังสามารถใช้กับ Primivitives ได้ด้วย
ในรูปเเบบ JSON จะสามารถใช้กับชนิดข้อมูล:
- Objects
{...} - Arrays
[...] - Primitives:
- Strings
- Numbers
- Boolean
true/false null
ยกตัวอย่าง เช่น:
// 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 เป็นการระบุข้อมูล(เท่านั้น) เฉพาะภาษา ฉะนั้น, บาง Object Properties ใน JavaScript จะถูกข้ามใน Method JSON.stringify
ยกตัวอย่าง เช่น:
- Function Properties (Methods)
- Symbolic Keys and Values
- Properties ที่เก็บ
underfined
let user = {
sayHi() { // ignored
alert("Hello");
},
[Symbol("id")]: 123, // ignored
something: undefined // ignored
};
alert( JSON.stringify(user) ); // {} (empty object)
โดยทั่วไปจะไม่มีปัญหา เเต่มันก็ไม่ตรงกับจุดประสงค์ของเรา ฉะนั้นเราจึงสามารถปรับเเต่งกระบวนการของมันได้ เเล้วสิ่งที่ดีคือ Nested Objects จะถูกเเปลงโดยอัตโนมัติ
ยกตัวอย่าง เช่น:
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"]},
}
*/
เเต่ก็มีข้อจำกัดคือ ห้ามมีการอ้างอิงเเบบวงกลม
การอ้างอิงเเบบวงกลม ยกตัวอย่าง เช่น:
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
ในกรณีข้างต้น การเเปลงของ Method JSON.stringify ไม่สามารถใช้งานได้ เนื่องจากมีการอ้างอิงเเบบวงกลม
room.occupiedByอ้างอิงmeetupmeetup.placeอ้างอิงroom
เเผนภาพการทำงานของกรณีข้างต้น
Excluding and transforming: replacer
Syntax เต็มของ JSON.stringify คือ
let json = JSON.stringify(value[, replacer, space])
ในกรณีข้างต้ประกอบด้วย:
- value - ค่าที่ต้องการเเปลง
- replacer - เป็นรูปเเบบ Arrays ของ Properties
function(key, value) - space - จำนวนช่องว่างที่ใช้ในรูปเเบบนั้นๆ
ส่วนมาก JSON.stringify ใช้กับกรณีข้างต้นเท่านั้น เเต่ถ้าเราอยากปรับเเต่งเเบบลงลึก เช่น การไม่นับ circular reference, เราสามารถใช้รูปเเบบบที่ 2 ของ Method JSON.stringify
ถ้าเราส่ง Array ของ properties เข้าไป, ส่วนที่ถูกส่งเข้าไป จะถูกแปลงเป็น JSON
ยกตัวอย่าง เช่น:
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":[{},{}]}
ในที่นี้ ข้อจำกัดของเราเยอะเกินไป, Properties List ถูกใช้กับทั้งโครงสร้าง Object ฉะนั้น Objects ใน participants ว่างเปล่า เพราะว่า name ไม่ได้อยู่ใน List
เรามารวม property มาไว้ใน List กัน ยกเว้น room.occupiedBy ที่จะทำให้เกิด circular reference
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}
}
*/
List ที่เราส่งเข้าไป มีความยาวนิดหน่อย
โดยปกติ เราสามารถใช้ function แทนที่จะเป็น Array ได้
function จะคืนค่าที่เป็น (key, value) ทุกตัวออกมา และควรจะคืนค่า “replaced” ที่จะใช้แทนตัวที่เป็นตัวเดิม หรือเป็น undefined ค่านั้นจะถูกข้าม
ในกรณีของเรา เราสามารถคืนค่า value ทุกอย่างได้ยกเว้น occupiedBy โดยถ้าเราต้องการที่จะละเว้น occupiedBy เราจะคืนค่า undefined
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
occupiedBy: [object Object]
*/
โปรดจำไว้ว่า replacer function จะเอา key/value ทุกตัวรวมถึงตัวที่ซ้อนลงไปด้วย โดย this ภายใน replacer เป็น Object ที่เก็บค่า property ปัจจุบันไว้
ในการเรียกใช้ครั้งแรก มันถูกสร้างโดยใช้ “wrapper object”: {"": meetup}. ในอีกความหมาย (key, value) ตัวแรกมี key ที่ว่างเปล่า นั่นคือเหตุผลที่บรรทัดแรกเป็น ":[object Object]" ในตัวอย่างด้านบน
Formatting: space
argument ตัวที่ 3 ของ JSON.stringify(value, replacer, space) เป็นตัวเลขที่จะเว้นวรรคเพื่อความสวยของการ format
ก่อนหน้านี้ String Object ไม่มีการเว้นวรรค นั่นโอเคดีถ้าเราแค่ต้องการส่งผ่าน network แต่ถ้าเราต้องการความสวยงามในการดู Output, argument ตัวนี้ก็ควรจะใช้
space = 2 ใช้บอก JavaScript ว่า เราต้องการจะแสดงผล nested objects โดยมีเว้นวรรค 2 ช่อง
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
}
}
*/
argument ตัวที่ 3 สามารถเป็น String ได้เหมือนกัน ในกรณีที่เราต้องการที่จะใช้ String แทนที่จะใส่ตัวเลขเพื่อบอกจำนวนที่จะเว้นวรรค
space ใช้เพื่อแค่มี Output ที่สวยงาม
Custom “toJSON”
เหมือนกับ toString Object อาจจะสามารถใช้ toJSON สำหรับ การแปลงไปเป็น to-JSON. JSON.stringify จะเรียกใช้งานโดยอัตโนมัติ ถ้ามันเรียกใช้ได้
ตัวอย่างเช่น
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)
}
*/
เราสามารถเห็นได้ว่า date (1) จะแปลงมาเป็น String โดยอัตโนมัติ เพราะว่า dates ทุกตัวมี toJSON method เพื่อใช้คืนค่า string
เรามาเพิ่ม toJSON เข้าไปใน 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
}
*/
เราเห็นได้ว่า toJSON ใช้สองรอบเมื่อเราเรียกใช้มันตรง ๆ JSON.stringify(room) และเมื่อเราเราเรียกใช้ room ใน Object ซ้อน
JSON.parse
ในการแปลงจาก JSON เป็น String เราต้องใช้ method JSON.parse
let value = JSON.parse(str[, reviver]);
str
จาก JSON เป็น String
reviver
function(key,value) ที่จะเรียก (key, value) และสามารถเปลี่ยนแปลงมันได้
ตัวอย่างเช่น
// stringified array
let numbers = "[0, 1, 2, 3]";
numbers = JSON.parse(numbers);
alert( numbers[1] ); // 1
หรือจะเป็น Object
let userData = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';
let user = JSON.parse(userData);
alert( user.friends[1] ); // 1
JSON อาจจะซับซ้อน Object และ Array อาจจะซ้อนกันไปเรื่อย ๆ แต่ว่า JSON ต้องอยู่ใน format เดียวกัน
นี่คือข้อผิดพลาดที่เจอได้บ่อย ๆ
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
}`;
JSON ไม่สนับสนุนการมี comments การเพิ่ม comments เข้าไปจะทำให้เกิดข้อผิดพลาดได้
JSON ปกตินั้นเข้มงวดมาก ไม่ใช่เพราะว่า developers ขี้เกียจ แต่เพื่อให้ใช้งานอัลกอริธึมการแยกวิเคราะห์ได้ง่าย เชื่อถือได้ และรวดเร็วมาก
Using reviver
จิตนาการว่าเราได้ meetup Object ที่เป็น String มาจาก Server
แบบนี้
// title: (meetup title), date: (meetup date)
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
เราต้องการที่จะแปลงมันเป็น JavaScript Object
เรามาทำมันโดยใช้ JSON.parse:
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str);
alert( meetup.date.getDate() ); // Error!
เกิด Error ขึ้น!!
ค่าของ meetup.date เป็น String ไม่ใช่ Date Object เราจะรู้ได้ไงว่ามันควรแปลงเป็น Date Object
เราจะใช้ reviving function ที่เป็น argument ที่สอง ที่จะคืนค่าทั้งหมดมา แต่ date จะมาเป็น 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!
เราสามารถใช้กับ Object ที่ซ้อนกันได้
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!
Summary
- JSON เป็นข้อมูล ที่มีมาตราฐานเป็นของตัวเอง และมี libraries รับรองสำหรับหลาย ๆ ภาษาโปรแกรมมิ่ง
- JSON รองรับ Objects, Arrays, Strings, Numbers, Booleans, and
null. - JavaScript มี method JSON.stringify เพื่อแปลงเป็น JSON และ JSON.parse เพื่ออ่าน JSON.
- ทั้งสอง methods รองรับการ function ในการเปลี่ยนแปลงเพื่อทำให้การอ่านการเขียน ฉลาดมากขึ้น
- ถ้า Object มี
toJSON, ดังนั้นมันจะใช้ได้โดยJSON.stringify.