JSON(JavaScript Object Notation),指JavaScript的对象表示法,它本身是个字符串,是一种数据交换格式,并非对象。通常所提的JSON对象实际是JSON字符串解析成对象的结果,或是浏览器window对象下的JSON对象。

而且JSON不止用于JavaScript中,其广泛用于数据交换。

#JSON和JavaScript对象

一个JSON文件,或一段JSON字符串,通常是这样的:

[{
    "name": "用户权限管理",
    "code": "99990002",
    "icon": "modicon-1",
    "items": [{
        "name": "模块权限",
        "code": "999900020009",
        "url": "",
        "isBlank": false,
        "items": [{
            "name": "向导模板",
            "code": "9999000200090003",
            "url": "pages/accLayoutTest.html",
            "isBlank": false,
            "items": []
        }, {
            "name": "模块管理我们的",
            "code": "9999000200090001",
            "url": "pages/contentPageTest.html",
            "isBlank": false,
            "items": []
        }]
    }]
}, ... ]

而这样类似的JavaScript对象则是这样的:

var menuData = [{
    name: "用户权限管理",
    code: "99990002",
    icon: "modicon-1",
    items: [{
        name: "模块权限",
        code: "999900020009",
        url: "",
        isBlank: false,
        items: [{
            name: "向导模板",
            code: "9999000200090003",
            url: "pages/accLayoutTest.html",
            isBlank: false,
            items: []
        }, {
            name: "模块管理我们的",
            code: "9999000200090001",
            url: "pages/contentPageTest.html",
            isBlank: false,
            items: []
        }]
    }]
}, ...]

两者非常的相似,所不同的是就是JavaScript对象中属性名,也就是对象的key值是可以没有引号的,其值为字符串时,使用''""包裹均可。 因此我们很多人将第一段代码块里所写的JSON称为JSON对象,实际上,它并不是一个对象,只是一个单纯的字符串而已,但是它符合JSON的语法规则,可以很方便地转化为JavaScript对象,或者方便地用于数据交换。

以下我们来了解一下JSON

#JSON语法

  • JSON语法规则

    JSON 语法是 JavaScript 对象表示语法的子集,其基本原则如下:

    • 数据在键值对中

    • 数据由逗号分隔

    • 花括号保存对象

    • 方括号保存数组

  • JSON的值

    • 数字(整数或浮点数)

    • 字符串(在双引号中)

    • 逻辑值(truefalse

    • 数组(在方括号中[]

    • 对象(在花括号中{}

    • null

JSON作为一种数据交换格式,为了保证其能被正确方便的解析,其格式有严格的要求,必须遵循以下规则:

  1. 复合类型的值只能是数组对象,不能是函数、正则表达式对象、日期对象。

  2. 简单类型的值只有四种:字符串、数值(必须以十进制表示)、布尔值和null(不能使用NaN, Infinity, -Infinityundefined)。

  3. 字符串必须使用双引号表示,不能使用单引号。

  4. 对象的键名必须放在双引号里面。

  5. 数组或对象最后一个成员的后面,不能有逗号。

  6. 数值前不能加0。

以下是合法的JSON格式示例:

["one", "two", "three"]

{
    "one": 1,
    "two": 2,
    "three": 3
}

{
    "names": [
        "张三",
        "李四"
    ]
}

[
    { "name": "张三" },
    { "name": "李四" }
]

下面这些就是不合法的:

{
    name: "张三",
    'age': 32
} // 属性名必须使用双引号

[32, 64, 128, 0xFFF] // 不能使用十六进制值

{
    "name": "张三",
    "age": undefined
} // 不能使用undefined

{
    "name": "张三",
    "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
    "getName": function() {
        return this.name;
    }
} // 不能使用函数和日期对象

{
    "name": "李四",
    "age": 018
} // 数值前不能有0

以上代码中为了指出错误所在,使用了JavaScript的注释法,实际JSON中是不能有注释的。

不合法的JSON会在解析成JavaScript对象时,出现错误。

#JSON和JavaScript对象的转化

#window.JSON

JSON作为一种数据交换格式,被写入了ECMAScript 5,在IE8及之后的浏览器都提供了一个JSON对象,用于对JSON进行解析和序列化。

#JSON.parse()

此方法接收一个JSON字符串,返回解析后的JavaScript对象,通常为ObjectArray

// JSON数据
var humansData = '[{"name":"zs","age":28},{"name":"ls","age":26}]';
// 解析为JavaScript对象
var humans = JSON.parse(humansData);

// 之后就可以访问其元素或属性了
humans[1].name; // ls
humans[1].age; // 26

如果传入不合法的JSON,则会在JSON.parse时报错。

为什么我们在ajax请求中,即使请求的数据为JSON,我们不用解析就能直接使用呢?

// test.text内容
/*
[{
	"name":"zs",
	"age":28
},{
	"name":"ls",
	"age":26
}]
*/

$.ajax({
    url: './test/test.text',
    dataType: 'JSON'
}).done(function(data){
    console.dir(data); // Array[2]
    console.log(data[0].name); // zs
    // 这里没有转化为js对象就能访问其属性?!    
});

这里实际是因为指定了dataType为JSON,从而进行了自动转化,所以能直接在成功回调中使用其属性。如果去掉dataType的指定,就不能直接访问其属性了,因为未转化时,其本身是一个字符串。第一行输出为test.text的内容,第二行输出undefined

#JSON.stringify()

此方法可接收一个JavaScript值将转化为JSON字符串,此字符串可被JSON.parse还原。

var humans = [{
    "name": "zs",
    "age": 28,
    "birth": new Date()
}, {
    "name": "ls",
    "age": 26,
    "birth": new Date()
}];

// 转化为JSON字符串
JSON.stringify(humans);
// "[{"name":"zs","age":28,"birth":"2016-10-25T07:24:11.701Z"},{"name":"ls","age":26,"birth":"2016-10-25T07:24:11.701Z"}]"

前面我们讲到了JSON中并非支持所有的JavaScript类型,因此此方法对一些JSON不可接受的值有所处理:原始对象中,如果有一个成员的值是undefined、函数或XML对象,这个成员会被省略。如果数组的成员是undefined、函数或XML对象,则这些值被转成null。

我们还发现,JSON中是不支持Date对象的,而上述转化为字符串的结果中正确包含了birth的值,其为一个日期格式的字符串。这是因为在Date对象上有一个名为toJSON的方法,JSON.stringify在序列化时,实际是调用了这个方法来输出结果的。

如果一个被序列化的对象拥有 toJSON 方法,那么该 toJSON 方法就会覆盖该对象默认的序列化行为:不是那个对象被序列化,而是调用 toJSON 方法后的返回值会被序列化,例如:

var obj = {
    foo: 'foo',
    toJSON: function() {
        return 'bar';
    }
};

JSON.stringify(obj); // '"bar"' 
JSON.stringify({x: obj}); // '{"x":"bar"}'

了解即可,详见: MDN:JSON.stringify(){.doc-link}

我们可能经常看到的是JSON.stringify( obj , null , 4),这样是什么意思呢?

对于上例,替换最后一句,结果如下所示:

JSON.stringify(humans, null, 4);
// "[
//     {
//         "name": "zs",
//         "age": 28,
//         "birth": "2016-10-25T07:24:11.701Z"
//     },
//     {
//         "name": "ls",
//         "age": 26,
//         "birth": "2016-10-25T07:24:11.701Z"
//     }
// ]"

其实没什么变化,不过是加上了空格缩进,使得可读性更高了。

其余两个参数的说明如下:

  • 第二个参数可指定序列化时的操作。

    • 如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;

    • 如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;

    • 如果该参数为null或者未提供,则对象所有的属性都会被序列化;

  • 第三个参数用于指定缩进用的空白字符串,用于美化输出(pretty-print);

    • 如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格;

    • 如果该参数为字符串(字符串的前十个字母),该字符串将被作为空格;

    • 如果该参数没有提供(或者为null)将没有空格。

#替代方法

window.JSON对象下虽然提供了完整的JSON字符串和JavaScript对象的转换方法。但是在IE8(兼容模式)以及更低版本的IE下没有提供这个对象,因此我们需要一些替代方案。

  • jQuery中提供了parseJSON这样一个方法来替代JSON.parse,它接收一个标准格式的JSON字符串,返回一个解析后的JavaScript对象。

  • 使用http://www.json.org/提供了一个json.js,这样ie8(兼容模式),ie7和ie6就可以支持JSON对象以及其stringify()parse()方法; 可以在https://github.com/douglascrockford/JSON-js上获取到这个js,一般现在用json2.js。

  • 还可以使用 eval('(' + jsonstr + ')') ; 来将json字符串转换成json对象,注意需要在json字符外包裹一对小括号。但最好不要使用这种方式,因为这种方式不安全,eval会将JSON字符串作为JavaScript语句来执行,JSON中的风险代码将被执行。

#参考链接

JSON

JSON对象

根本没有“JSON对象”这回事!

JavaScript标准库-JSON对象

js对象与JSON字符串互转