有时候我们需要将Javascript对象转换为string用于网络传输,通常转换为字符串我们会手动实现toString()方法,例如:
let user = {
name: "John",
age: 30,
toString() {
return `{name: "${this.name}", age: ${this.age}}`;
}
};
alert(user); // {name: "John", age: 30}
但是如果这样写的话会出现麻烦,比如我们添加或移除属性的时候就要重写toString方法,这就很是头疼。因此,Javascript提供了JSON机制来将对象转换为字符串,也就是JSON.stringify()
JSON.stringify()
案例
JSON.stringify()返回对象的json格式,也就是字符串的格式,例如:
et student = {
name: 'John',
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": "John",
"age": 30,
"isAdmin": false,
"courses": ["html", "css", "js"],
"wife": null
}
*/
要注意的是json格式必须使用双引号,不能是单引号或则反引号
原型的JSON转换
JSON除了转换对象外,也可以应用于原型类型,例如:
// 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.stringify()在转换对象的时候会默认忽略以下属性:
(1)方法属性,如function;
(2)Symbol属性;
(3)值为undefined的属性;
例如:
let user = {
sayHi() { // ignored
alert("Hello");
},
[Symbol("id")]: 123, // ignored
something: undefined // ignored
};
alert( JSON.stringify(user) ); // {} (empty object)
嵌套对象的JSON转换
当需要转换的对象中嵌套了其他的对象或者数组,JSON同样也支持转换,例如:
let meetup = {
title: "Conference",
room: {
number: 123,
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
这里因为meetu.place引用了room,而room.occupiedBy引用了meetup,存在循环引用问题,故报错。
JSON.stringify()其余的两个参数
JSON.stringify()具体的语法如下:
let json = JSON.stringify(value[, replacer, space])
第一个参数我们都知道了,现在来讲解第二个参数replacer。replacer参数可以是一个数组或者是一个函数function(key,value),它表示需要转换为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":[{},{}]}
这里如果不使用replacer参数指定要转换的属性的话,则会出现循环引用的错误,但由于我们指定了title和participants两个属性,故stringify()就只转换这两个属性。需要注意的是此时participants的值为空,如果想不为空就必须把name属性名也指定,例如下面这个例子:
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}
}
*/
以数组的形式来指定属性非常繁琐,如果是一个复杂对象的话就要写一大串属性名,那真是头疼。幸运的是,我们还可以使用隐射函数function(key,value)来指定属性名,例如:
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}`); // to see what replacer gets
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
*/
stringify()遍历meetup所有键值对的时候会调用function replacer(key,value),并根据该函数返回的属性值来确定要转换的属性,当replacer(key,value)返回undefined的时候,就说明该属性不需要被转换,因为stringify()不会转换值为undefined的属性
现在我们来讲解第三个属性space,space用来指定嵌套对象的属性前面的空格数量,它是转换后的字符串格式更好看,看下面例子就明白了:
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
}
}
*/
自定义toJSON()方法
就像自定义toString()方法一样,我们同样可以定义toJSON()方法,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属性的值变成了字符串,那是因为Date对象内部实现了toJSON()方法来放回字符串格式的date对象内容。
对于普通对象,我们可以手动来实现toJSON()方法,例如:
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
}
*/
JSON.parse()
JSON.parse()是JSON.stringify()的逆过程,用来解析json字符串并转换为对应的对象,它的具体语法如下:
let value = JSON.parse(str[, reviver]);
其中str是要被解析的json格式字符串,revier是可选的函数参数function(key,value),更stringify()的replacer参数很像,看下面例子:
// stringified array
let numbers = "[0, 1, 2, 3]";
numbers = JSON.parse(numbers);
alert( numbers[1] ); // 1
嵌套对象如下例子:
let user = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';
user = JSON.parse(user);
alert( user.friends[1] ); // 1
这里介绍第二个参数reviver,parse()解析每个键值对的时候会自动调用revier函数来确定是否解析此键值对,这里有一个报错的例子:
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str);
alert( meetup.date.getDate() ); // Error!
显然,JSON.parse()不知道date属性需要转换为Date对象,所有我们要实现revier函数参数来告诉它,例如:
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!
对于嵌套对象也一样,例如:
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!