最近做个小功能,要把一个文件解析成JSON
格式,用JavaScript的JSON.stringify
转换成字符串并写入文件后,在Sublime中查看时变成一行了,可读性很差,只得放在json.cn这个网站上查看。有时文件内容特别多,在json.cn上查看也不方便,于是想自己写个JSON
美化的函数。
比如一个JavaScript对象:
var obj = {
a: 3,
b: "string",
list1: [
{
name: "name1",
age: 25
},
{
name: "name2",
age: 22
}
],
list2: [1,2,3],
book: {
name: "c",
}
};
如果用JSON.stringify
转换成字符串,结果是:
{"a":3,"b":"string","list1":[{"name":"name1","age":25},{"name":"name2","age":22}],"list2":[1,2,3],"book":{"name":"c"}}
上面的很紧凑,可是层次不明显。我想看到的是:
{
a: 3,
b: "string",
list1: [
{
name: "name1",
age: 25
},
{
name: "name2",
age: 22
}
],
list2: [
1,
2,
3
],
book: {
name: "c"
}
}
JSON
可以表示出复杂的结构,不过复杂的结构也是有基本的类型组合而成。要把一个对象用起字符串形式表示出来,先看基本类型怎么表示。
boolean
的true
和false
,可以用字符串true和false表示,不过用该对象的toString()
方法更快捷;number
类型的值,返回原值,当与字符串相加时,JavaScript会自动转换成字符串,也可以用toString()
方法。string
类型的值,需要在头尾添加双引号"
。对于嵌套元素,则要考虑缩进,每嵌套一层,缩进增加(比如4个空格)。
复合数据类型有对象和数组。
先看看对象:
对象是由一个或多个键-值对组成的集合,对象的字符串表示是由”{” + 键名字 + “:” + 值的字符串表示 + “,” + “}” 组成,如果是只有一个键值对,或者是最后一个键值对,那么最后一个 “,”不要。由于值可以是任意类型,所以计算值的字符串表示时,要递归调用,递归时缩进要增加。考虑缩进时,对一个对象迭代过程中,可以把每个 (键 + “:” + 值的字符串表示)作为一个数组元素加入数组 【见上图】,然后把各数组元素用 (”,” + 换行符 + 缩进空格)连起来,最后在两边加上“{”与 “}”。
数组更简单了,对每个元素递归调用,然后用跟对象类似的方法把各个元素连起来即可。
光说无用,还是看代码比较清楚。原来有70多行,优化精简后只有43行。
function prettyPrint(obj) {
var ENDLINE = "\n";
var COMMA_ENDLINE = ",\n";
var OBJ_BEGIN = "{";
var OBJ_END = "}";
var ARR_BEGIN = "[";
var ARR_END = "]";
var INDNET_SPACES = 4;
return (function innerPrettyPrint(obj, spaces) {
var type = typeof obj;
if (type == "number" || type == "boolean") {
return obj.toString();
} else if (type == "string") {
return '"' + obj + '"';
} else {
var entries = [];
var thisIndent = ' '.repeat(spaces);
var subIndent = thisIndent + ' '.repeat(INDNET_SPACES);
var subSpaces = spaces + INDNET_SPACES;
if (Object.prototype.toString.call(obj) == '[object Object]') {
for(var k in obj) {
entries.push(k + ': ' + innerPrettyPrint(obj[k], subSpaces));
}
return OBJ_BEGIN + ENDLINE + subIndent + entries.join(COMMA_ENDLINE + subIndent) + ENDLINE + thisIndent + OBJ_END;
} else if (Object.prototype.toString.call(obj) == '[object Array]') {
obj.forEach(function(a) {
entries.push(innerPrettyPrint(a, subSpaces));
});
return ARR_BEGIN + ENDLINE + subIndent + entries.join(COMMA_ENDLINE + subIndent) + ENDLINE + thisIndent + ARR_END;
} else if (obj === null) {
return "null";
} else {
return obj.toString();
}
}
})(obj, 0);
}
逻辑很清晰,不解释。
测试如下一个对象:
var obj = {
a: true,
b: false,
c: 0,
b: 1,
d: 2.0,
f: "this a string",
g: null,
list1: [
{
name: "jack",
age: 25
},
{
name: "lee",
age: 22
}
],
list2: [1,2,3],
book: {
title: "book",
author: "me"
}
};
测试代码:
console.log(prettyPrint(obj));
输出:
{
a: true,
b: 1,
c: 0,
d: 2,
f: "this a string",
g: null,
list1: [
{
name: "jack",
age: 25
},
{
name: "lee",
age: 22
}
],
list2: [
1,
2,
3
],
book: {
title: "book",
author: "me"
}
}
有了这个工具,对于内容特别大的JSON
对象,可以轻松地在文本编辑器中查看了。