javascript学习笔记 (15)--JSON序列化&反序列化

什么是JSON

JSON是JavaScript Object Notation的缩写,它是一种数据交换格式
在JSON中,有如下数据类型
number
boolean
string
null
array
object
JSON规定了字符集必须是UTF-8,可以表示多语言
为了统一解析,JSON的字符串规定必须用双引号"",Object的键也必须用双引号""

把任何JavaScript对象变成JSON,就是把这个对象序列化成一个JSON格式的字符串,这样才能够通过网络传递给其他计算机。 如果我们收到一个JSON格式的字符串,只需要把它反序列化成一个JavaScript对象,就可以在JavaScript中直接使用这个对象了

序列化

让我们先把小明这个对象序列化成JSON格式的字符串

var xiaoming = { 
 name: '小明', 
 age: 14, 
boy:true,
 grade: null, 
 middle_school: " Middle School", 
 skills: ['JavaScript', 'Java', 'Python']
  };
JSON.stringify(xiaoming);//"{\"name\":\"小明\",\"age\":14,\"boy\":true,\"grade\":null,\"middle_school\":\" Middle School\",\"skills\":[\"JavaScript\",\"Java\",\"Python\"]}"

要输出得好看一些,可以加上参数,按缩进输出

JSON.stringify(xiaoming, null, ' ');
"{
 \"name\": \"小明\",
 \"age\": 14,
 \"boy\": true,
 \"grade\": null,
 \"middle_school\": \" Middle School\",
 \"skills\": [
  \"JavaScript\",
  \"Java\",
  \"Python\"
 ]
}"

第一个参数就是我们需要转化的对象,就不多说了

第二个参数有两种情况
第一种情况,传入Array
如果我们只想输出指定的属性,可以传入Array,Array内存放我们需要的Key,然后他变只会转化我们需要的Key

JSON.stringify(xiaoming, ['name', 'skills'], ' ');
"{
 \"name\": \"小明\",
 \"skills\": [
  \"JavaScript\",
  \"Java\",
  \"Python\"
 ]
}"

JSON.stringify(xiaoming, ['name', 'skill'], ' ');
"{
 \"name\": \"小明\"
}"
JSON.stringify(xiaoming, ['nam', 'skill'], ' ');
"{}"

如果传入的key不存在,那么他就只会输出存在的key和值

第二种情况,传入一个函数
一般这个函数的参数就是key和value,这样可以对对象的key和value进行处理

 function convert(key, value) { 
 if (typeof value === 'string') 
 return value.toUpperCase();
 return value;
; }
 JSON.stringify(xiaoming, convert, ' ');
"{
 \"name\": \"小明\",
 \"age\": 14,
 \"boy\": true,
 \"grade\": null,
 \"middle_school\": \" MIDDLE SCHOOL\",
 \"skills\": [
  \"JAVASCRIPT\",
  \"JAVA\",
  \"PYTHON\"
 ]
}"

上面的代码把所有属性值都变成大写

这一点网上都提到过,但是有一点我当时很不理解,为什么会有两个return呢?
关于这一点,我也只是个人的理解,也没有什么依据,如果有知道的可以帮忙解答一下
我们看下面几个代码
为了方便,我们把xiaoming压缩一下

var xiaoming = { 
 name: '小明', 
 age: 14
  };
function convert(key, value) {console.log(key);console.log(value);}

JSON.stringify(xiaoming, convert, ' ');
<empty string> 
Object { name: "小明", age: 14 }
undefined

这个返回结果我们是意想不到的
本来我们希望通过这个函数,可以返回每个key和对应的value,但是却返回了一个空字符串和xiaoming这个对象
因为xiaoming是个对象,按理来说是没有key,所以这里的key就是个空字符串,第二个返回的就是xiaoming对象,也就是我们的value
这一点解释了,但为什么对象的key和value没有返回,也没有转化成 json?说明这里函数在执行完console.log(xiaoming)之后就断了,没有下一步的操作了

function convert(key, value) {console.log(key);console.log(value);return value;}

JSON.stringify(xiaoming, convert, ' ');
<empty string> 
Object { name: "小明", age: 14 }
name 
小明 
age 
14 
"{
 \"name\": \"小明\",
 \"age\": 14
}"

这一次,我们增加了个return value,发现这个正常执行了,并且通过这个我们可以感受到这个的执行顺序
如果我们给stringfy传入了函数,那么会循环遍历执行函数,当循环完之后再根据函数里的要求转化成json对象,并且key和value是从对象开始,直到对象的最后一个属性

那么问题又来了,为什么一个return会有这么大的效果差别

第一个函数没有return,所以就会有return undefined的效果,结果我们的这个JSON转化不执行了,然后第二个函数,再console.log(xiaoming)后return xiaoming,不是undefined了,那么就可以正常执行下去了

这个就是精华所在,第二个参数传入函数进行取舍的原理就是靠返回值决定的,如果返回undefined那么就会跳过该键值对,转而下一个,而如果你第一个键值对也就是我们的对象就传回了undefined,那么他就不会继续遍历他的属性了,直接返回一个undefined,所以我认为,return value的主要原因就是防止再遍历到我们第一个键值对也就是xiaoming本身就直接不执行了

测试一下我的猜想

function convert(key, value) {console.log(key+':'+value);if(!key==true)return value;}
JSON.stringify(xiaoming, convert, ' ');
:[object Object] ;//这个地方是因为没有展开..所以只返回了object
name:小明 
age:14 
"{}"

ok,证明我们猜想对了,因为xiaoming的key是空字符串,而!""==true,而一般属性的key又不能为空,所以只会在遍历到xiaoming本身时候返回value,其他的时候都是undefined,都被过滤掉了,也就返回了{}空对象

那么还有一个问题,为什么一定要返回value
这个也是按着我自己的理解吧,我们可以看这个返回值到底去哪了

var xiaohong ={sex:"girl"}
function convert(key, value) {if(!key==true)return xiaohong;else return value;}
JSON.stringify(xiaoming, convert, ' ');
"{
 \"sex\": \"girl\"
}"

xiaoming变成xiaohong了

function convert(key, value) {if(!key==true)return 1;else return value;}
JSON.stringify(xiaoming, convert, ' ');
"1"

只有1了
这说明,return返回值返回到了每个键值对的value里面,比如说我们这里的第一个键值对,如果返回的是一个新对象,那么之后的遍历就跟之前的xiaoming没关系了,就在xiaohong下面遍历了,如果直接干脆返回的不是对象了,那么之后也无法遍历了
不过这个操作只限于我们的第一个键值对,也就是对象,如果这个地方不设置好的话,要不是执行不了,要不就是不是我们想要的对象了
当然后面的键值对的话返回的值就是我们等会转化后的结果,就可以利用这个更新value的方法进行我们的操作
key这里是无法修改的啦

好啦道理讲完啦,再回到刚才我们的函数

第二个参数如果不传的话,可以写null

 function convert(key, value) { 
 if (typeof value === 'string') 
 return value.toUpperCase();
 return value;
; }

这个的效果其实和下面这个是一样的,因为只可能return一次嘛

 function convert(key, value) { 
 if (typeof value === 'string') 
 return value.toUpperCase();
else  return value;
; }

所以效果也很明显了,只会把所有字符串改为大写

精确序列化

如果我们还想要精确控制如何序列化小明,可以给xiaoming定义一个toJSON()的方法,直接返回JSON应该序列化的数据

var xiaoming = { 
name: '小明', 
age: 14, 
boy: true,
 height: 1.65, 
 grade: null,
  middle_school: " Middle School", 
  skills: ['JavaScript', 'Java', 'Python'],
   toJSON: function () { return 
   { 
   'Name': this.name,
 'Age': this.age }; } };
JSON.stringify(xiaoming); // "{\"Name\":\"小明\",\"Age\":14}"

toJSON其实相当于新建了个对象,可以跟xiaoming一点关心都没有

var xiaohong={
toJSON:function(){
return 
{name:"xiaoming",
age:18,
sex:"name"
};
}
}
JSON.stringify(xiaohong);
"{\"name\":\"xiaoming\",\"age\":18,\"sex\":\"name\"}"

所以这个比较好操作,key和value都可以修改,甚至可以添加key和value

反序列化

拿到一个JSON格式的字符串,我们直接用JSON.parse()把它变成一个JavaScript对象

JSON.parse('[1,2,3,true]'); // [1, 2, 3, true] 
JSON.parse('{"name":"小明","age":14}'); // Object {name: '小明', age: 14}
 JSON.parse('true'); // true 
 JSON.parse('123.45'); // 123.45 

JSON.parse()还可以接收一个函数,用来转换解析出的属性

 JSON.parse('{"name":"小明","age":14}', function (key, value) { if (key === 'name') { return value + '同学'; }
 else return value; 
 });
Object {name: '小明同学', age: 14} 

再来简单分析一下这个传入函数的原理吧

JSON.parse('{"name":"小明","age":14}', function (key, value) { console.log(key);console.log(value); return value;})
name 
小明 
age 
14 
<empty string> 
Object { name: "小明", age: 14 }
Object { name: "小明", age: 14 
JSON.parse('{"name":"小明","age":14}', function (key, value) {if(!key==true)return xiaohong; else {console.log(key);console.log(value); return value;}})
name 
小明
age 
14 
Object { sex: "girl" }

这个跟刚才的顺序是相反的,先是从内部的元素开始,最后才是我们的整个对象,所以在这里的话如果修改我们的对象了,就会反序列化出来另一个对象,但其实如果在中间提取键值对的化还是我们传入的那个对象

JSON.parse('{"name":"小明","age":14}', 
function (key, value) { 
if(key=="name")return"xiaobai";
console.log(key);console.log(value); 
return xiaohong;})
age
14 
<empty string>
Object { name: "xiaobai", age: {} }
Object { sex: "girl" }

可以看到,我们在前面修改的name值,修改的对象的属性值,其实直接把传入的参数里面的属性值也修改了,所以我们最后一个键值对也就是我们的对象的value也被修改了,但最后我们得到的反序列化结果还是要看我们最后一个传入的对象的值,如果return value,就是之前我们修改了后的传入对象,如果return其他的,那么前面的修改就没有效果啦…就返回其他

猜你喜欢

转载自blog.csdn.net/azraelxuemo/article/details/106904046