美化打印一个JSON对象

一、前言

最近做个小功能,要把一个文件解析成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可以表示出复杂的结构,不过复杂的结构也是有基本的类型组合而成。要把一个对象用起字符串形式表示出来,先看基本类型怎么表示。

基本数据类型

  • booleantruefalse,可以用字符串true和false表示,不过用该对象的toString()方法更快捷;
  • number类型的值,返回原值,当与字符串相加时,JavaScript会自动转换成字符串,也可以用toString()方法。
  • string类型的值,需要在头尾添加双引号"
  • 特例,如果是null,返回字符串null

对于嵌套元素,则要考虑缩进,每嵌套一层,缩进增加(比如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对象,可以轻松地在文本编辑器中查看了。

你可能感兴趣的:(大前端)