尝试写一下对象的序列化

问题: 如何把一个object序列化的显示?这种需求其实还是很多的, 主要都是为了方便查看object.

比如一个object , {“name”:”lucyhao”,”age”:”18”}, 显示成如下

1
2
3
4
{
"name" : "lucyhao",
"age" : "18"
}

首先说明一下JSON.stringify已经把序列化的工作做了,并且很全面,在实际的需求中,能够使用就不要自己写了.

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

  • 解析object/array, 基本思路,读取 object和key和value, 把key和value组合成一个字符串放到数组中,最后把数组连接起来,比如[‘“name”:”lucyhao”\n’,’”age”:”18”\n’].join(‘’). 如果在解析的过程中遇到object/array, 递归的解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var level = 0;
var space = " ";//解析出来每一行前面的格式
var beautify = function(value){
var _str = value;
level++;
var _l = level;
var tab = "";
var endTab = "";
//每一层(因为会递归的计算)的起始字符与结束字符
while(_l > 0){
if(_l > 1){
endTab += space;
}
tab += space;
_l --;
}

//中间的解析部分(object/array的解析)
var jsonStr = [];

level--;
return jsonStr.join("");
}

中间的解析部分, 就是对object/array的解析

(1)object的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var _beautifyJSON = function(value,tab,endTab){

var _str = value;
var jsonStr = [];
jsonStr.push("{\n");//开头的花括号

for(var key in _str){
//只解析可枚举的属性

if(typeof(_str[key]) == "object"){
//如果是对象,递归调用对象的解析
var object = beautify(_str[key]);
if(object === false){
return false;
}else{
jsonStr.push(tab+'"'+key+'":'+object+"\n");
}
}else{
//如果不是,加入到数组中
jsonStr.push(tab+'"'+key+'":'+beautifyType(_str[key])+"\n");
}
}
jsonStr.push(endTab+"}");//结尾的花括号
return jsonStr;
}

(2)array的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var _beautifyJSONArray = function(value,tab,endTab){
var _str = value;
var jsonStr = [];
jsonStr.push("[\n");
for(var i = 0 ;i<_str.length ;i++){

if(typeof(_str[i]) == "string" || typeof(_str[i]) == "number"){
//字符串或者数据,直接加入数组
if(i == _str.length -1){
jsonStr.push(tab+beautifyType(_str[i])+"\n")
}else{
jsonStr.push(tab+beautifyType(_str[i])+",\n")
}

}else{
//对象,递归的调用对象的解析
var object = beautify(_str[i]);
if(object === false){
return false;
}else{
if(i == _str.length -1){
jsonStr.push(tab+object+"\n")
}else{
jsonStr.push(tab+object+",\n")
}
}
}
}
jsonStr.push(endTab+"]");
return jsonStr;
}

其实上面已经完成了核心的功能, 但是如何这是一道面试官出的题, 这个时候面试官就会说, 你再考虑下有没有什么情况没考虑到. 看下JSON.stringify规范http://www.ecma-international.org/ecma-262/6.0/#sec-json.stringify

就会发现, 除了算法, 里面还有NOTE1 - NOTE6

NOTE1 : 要注意循环的情况. 如果有循环, 需要抛出错误.
1
2
3
a = [];
a[0] = a;
my_text = JSON.stringify(a); //This must throw a TypeError.

所以我们还需要考虑循环的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function isCircle(a,aStack){
aStack = aStack || [];
var length = aStack.length;
while(length--){
if(aStack[length] === a){
return true;
}
}
aStack.push(a);
var keys = getkeys(a);
length = keys.length;
while (length--) {
var key = keys[length];
if (isCircle(a[key],aStack)){
return true;
}
}
aStack.pop();

return false;
}
NOTE2: 符号的显示
  • null 会解析成字符串null显示
  • true/false 会解析成字符串true/false显示
  • undefined ,不会解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//解析json的时候,对不同的类型进行不同的显示;大多数类型都转换成string进行显示
//Object.prototype.toString 可以判断是什么类型
var beautifyType = function(value){
var type = Object.prototype.toString.call(value);
switch (type) {
case '[object String]':
case '[object Boolean]':
case '[object Null]':
case '[object RegExp]':
case '[object Date]':
return '"'+value+'"';
case '[object Number]':
if(isNaN(value) || value === Infinity){
return "null";
}else{
return +value;
}

case '[object Array]':
case '[object Object]':
return value;
case '[object Undefined]':
case '[object Function]':
return undefined; //在外层判断是undefined,不进行解析;也可以在外层判断出undefined之后,就不进行类型转换显示
}
NOTE3 : 字符串包裹在””之间显示; 双引号和右斜线前加\;控制字符做转义显示\uHHHH,或者用短些形式,比如\b,\f,\n,\r,\t (此条中对于控制字符的,自己没有给出例子,所以实在不知如何写)
1
2
3
4
5
6
//对于字符串包裹在双引号之类的,见note2
//对于双引号加\的
value.replace(/\"/g,'\\\"');
//对于\ 变\\的,因为\本身是转义符,如果要有"\"符号本身,js 应该\\这样写,
//var a = "\sdfsfd"; 其实 a = "sdfsfd" 如果要表达\字符的意思,应该是var a = "\\sdfsfd";
//所以看起来好像也不用做什么,因为我们是在用js来做这个算法
NOTE4: NaN 和Infinity会被解析成字符串null (见note2)
NOTE5: undefined和function这类,不会解析 (见note2)
NOTE6: 对于最后json解析出来呈现的样子,规定了左花括号开始,属性用逗号隔开,右花括号结束。属性后面带冒号,属性值。数组左方括号,值之间用逗号隔开,又方括号

最近被问好多比如如何判断类型这样的问题,突然有个需求把json字符串解析出来显示,就尝试自己写一下. PS : 还是JSON.stringify考虑的全,还可以往里面传function,指名解析哪些属性等。