js学习笔记:JSON

曾经,XML是互联网上传输结构化数据的事实标准。但是XML太过繁琐、冗长。为解决这个问题,JSON出现了。JSON是js的一个严格的子集,利用了js中的一些模式来表示结构化数据。关键JSON是一种数据格式,而不是一种编程语言,虽然有相同的语法形式,但JSON并不从属于js,很多语言都有针对JSON的解析器和序列化器。

语法

JSON的语法可以表示以下三种类型的值:

  • 简单值:使用与js相同的语法,可以在JSON中表示字符串、数值、布尔值和null。但JSON不支持特殊值undefined。
  • 对象:对象作为一种复杂数据类型,表示的是一组无序的键值对儿。而每个键值对中的值可以是简单值,也可以是复杂数据类型的值。
  • 数组:数组也是一种复杂数据类型,表示一组有序的值的列表,可以通过数值索引来访问其中的值。数组的值也可以是任意类型——简单值、对象或数组。

JSON不支持变量、函数或对象实例,它就是一种表示结构化数据的格式。

简单值

最简单的JSON数据形式就是简单值。如:

5  //数值
"hello world"   //字符串

js字符串与JSON字符串最大区别就是,JSON字符串必须使用双引号(单引号会导致语法错误)。

布尔值和null也是有效的JSON形式。

但是在实际应用中,JSON更多地用来表示更复杂的数据结构,而简单值只是整个数据结构中的一部分。

对象

JSON中的对象与js字面量稍微有一些不同。
下面是一个js中的字面量:

var person = {
    name:"nicholas",
    age:29
};

在JSON中的对象要求给属性加双引号,JSON表示形式如下:

{
    "name":"nicholas",
    "age":29
}

与js的对象字面量相比,JSON对象有几个地方不一样:

  • 没有声明变量(JSON中没有变量的概念)。
  • 没有末尾的分号(因为这并不是js语句,不用加分号)
  • 对象的属性必须加双引号

属性的值可以是简单值,也可以是复杂类型值,因此可以像下面这样在对象中嵌入对象:

{
    "name":"nicholas",
    "age":29,
    "school":{
        "name":"college",
        "location":"NY"
    }
}

数组

JSON中的第二种复杂数据类型是数组。JSON数组采用的就是js中的数组字面量形式。
下面是js中的数组字面量:

var values = [25,"hi",true];

在JSON中,可以采用同样的语法表示同一个数组:

[25,"hi",true]

同样要注意,JSON数组也没有变量和分号。
把数组和对象结合起来,可以构成更复杂的数据集合:

[
    {
        "title":"aaa",
        "authors":[
            "aa"
        ],
        "edition":3,
        "year":2011
    },
    {
        "title":"bbb",
        "authors":[
            "aa",
            "bb"
        ],
        "edition":2,
        "year":2009
    }
]

解析与序列化

JSON之所以流行,因为可以把JSON数据结构解析为有用的js对象。与XML数据结构要解析为DOM文档而且从中提取数据极为麻烦相比,JSON可以解析为js对象的优势极其明显。

JSON对象

早期的JSON解析器基本上就是使用js的eval()函数。由于JSON是js语法的子集,因此eval()函数可以解析、解释并返回js对象和数组。

ECMAScript5对解析JSON的行为进行规范,定义了全局对象JSON。
JSON对象有两个方法:

  • stringify():把js对象序列化为JSON字符串
  • parse():把JSON字符串解析为原生js值。
var book = {
        "title":"aaa",
        "authors":[
            "aa"
        ],
        "edition":3,
        "year":2011
};
var jsonText = JSON.stringify(book);
//"{"title":"aaa","authors":["aa"],"edition":3,"year":2011}"

这个例子使用JSON.stringify( )把一个js对象序列化为一个JSON字符串,然后将它保存在变量jsonText中。
默认情况下,JSON.stringify()输出的JSON字符串不包含任何空格字符或缩进,就像上面展示的那样。

在序列化js对象时,所有函数及原型成员都会被有意忽略,不体现在结果中。此外,值为undefined的任何属性也都会被跳过。最终结果中的值都是有效的JSON数据类型。

将JSON字符串直接传递给JSON.parse()就可以得到相应的js值。

var bookCopy = JSON.parse(jsonText);

bookCopy是一个与book类似的对象。虽然book与bookCopy有相同的属性,但它们两个是独立的,没有任何关系的对象。

如果传给JSON.parse()的字符串不是有效的JSON,该方法会抛出错误。

序列化选项

实际上,JSON.stringify()除了要序列化的js对象外,还可以接受另外两个参数,这两个参数用于指定以不同的方式序列化js对象。
第一个参数是一个过滤器,可以是一个数组,也可以是一个函数。
第二个参数是一个选项,表示是否在JSON字符串中保持缩进。

过滤结果

如果过滤器参数是数组,那么JSON.stringify()的结果中将只包含数组中列出的属性。

var book = {
        "title":"aaa",
        "authors":[
            "aa"
        ],
        "edition":3,
        "year":2011
};
var jsonText = JSON.stringify(book,["title","edition"]);

第二个参数是一个数组,包含两个字符串。这两个属性与将要序列化的对象中的属性是对应的,因此在返回的结果字符串中,就只会包含这两个属性:

"{"title":"aaa","edition":3}"

如果第二个参数是函数,行为会稍有不同。传入的函数接收两个参数,属性名和属性值。根据属性名可以知道应该要如何处理序列化的对象中的属性。属性名只能是字符串,而在并不是键值对儿的结构中,键名可以是空字符串。

为了改变序列化对象的结果,函数返回的值就是相应键的值。但是如果函数返回了undefined,那么相应的属性会被忽略。

var book = {
        "title":"aaa",
        "authors":[
            "aa",
            "bb"
        ],
        "edition":3,
        "year":2011
};
var jsonText = JSON.stringify(book,function(key,value){
    switch(key){
        case "authors":
            return value.join(",");
        case "year":
            return 5000;
        case "edition":
            return undefined;
        default:
            return value;
    }
});

这里,函数过滤器根据传入的键来决定结果。
如果键为“authors”,就将数组连接为一个字符串;
如果键为“year”,就将其值设置为5000;
如果键为“edition”,通过返回undefined删除该属性;
最后,一定要提供default项,此时返回传入的值,以便其他值都能正常出现在结果中。
序列化后的字符串如下:

"{"title":"aaa","authors":"aa,bb","year":5000}"

要序列化的对象中的每一个对象都要经过过滤器,因此数组中的每个带有这些属性的对象经过过滤之后,每个对象都只会包含”title”,”authors”,”year”这三个属性。

字符串缩进

JSON.stringify()方法的第三个参数用于控制结果中的缩进和空格符。
如果这个参数是一个数值,那它表示的是每个级别缩进的空格数。
例如,要在每个级别缩进4个空格:

var book = {
        "title":"aaa",
        "authors":[
            "aa",
            "bb"
        ],
        "edition":3,
        "year":2011
};
var jsonText = JSON.stringify(book,null,4);

结果如下所示:

{
    "title": "aaa",
    "authors": [
        "aa",
        "bb"
    ],
    "edition": 3,
    "year": 2011
}

可以注意到,这个方法也在结果字符串中插入了换行符以提高可读性。只要传入有效的控制缩进的参数值,结果字符串就会包含换行符。

最大缩进空格数为10,所有大于10的值都会自动转换为10.

如果缩进参数是一个字符串而非数值,则这个字符串将在JSON字符串中被用作缩进字符。在使用字符串的情况下,可以将缩进字符设置为制表符,或者两个短划线之类的任意字符。

var jsonText = JSON.stringify(book,null,"--");
{
--"title": "aaa",
--"authors": [
----"aa",
----"bb"
--],
--"edition": 3,
--"year": 2011
}

缩进字符串最长不能超过10个字符长。如果字符串长度超过了10个,结果中将只出现前10个字符。

toJSON()方法

有时候,JSON.stringify()方法还不能满足对某些对象进行自定义序列化的需求。这种情况下,可以给对象定义toJSON()方法,返回其自身的JSON数据格式。

可以为任何对象添加toJSON()方法。

var book = {
        "title":"aaa",
        "authors":[
            "aa",
            "bb"
        ],
        "edition":3,
        "year":2011,
        toJSON:function(){
            return this.title;
        }
};
var jsonText = JSON.stringify(book); //"aaa"

以上代码在book对象上定义了一个toJSON方法,该方法返回title的值。

可以让toJSON()方法返回任何值,他都能正常工作。
比如,在这个方法上返回undefined,此时如果包含它的对象嵌入在另一个对象中,会导致它的值变为null;而如果它是顶级对象,结果就是undefined。

toJSON()可以作为函数过滤器的补充,因此理解过滤器序列化的内部顺序十分重要。假设把一个对象传入JSON.stringify(),序列化该对象的顺序如下:

  • 如果存在toJSON()方法而且能通过它取得有效的值,则调用该方法,否则返回对象本身。
  • 如果提供了第二个参数,应用这个函数过滤器。传入函数过滤器的值是上一步的结果。
  • 对上一步返回的每个值进行相应的序列化。
  • 如果提供了第三个参数,执行相应的格式化。

解析选项

JSON.parse()方法也可以接收另一个参数,该参数是一个函数,将在每个键值对上调用。
为了区别JSON.stringify()接收的过滤函数,这个函数被称为还原函数,但实际上这两个函数的签名是相同的,它们都接收两个参数,一个键和一个值,而且都要返回一个值。

如果还原函数返回undefined,则表示要从结果中删除相应的键;
如果返回其他值,则将该值插入到结果中。

var book = {
        "title":"aaa",
        "authors":[
            "aa",
            "bb"
        ],
        "edition":3,
        "year":2011,
        "releaseDate":new Date(2011,11,1)
};
var jsonText = JSON.stringify(book);
var bookCopy = JSON.parse(jsonText,function(key,value){
    if(key == "releaseDate"){
        return new Date(value);
    }else{
        return value;
    }
});
alert(bookCopy.releaseDate.getFullYear());

以上代码先是为book增加了一个releaseDate属性,该属性保存着一个Date对象。这个对象在序列化之后变成了有效的字符串,然后经过解析又在bookCopy中还原为一个Date对象。(还原函数在遇到“releaseDate”键时,会基于相应的值创建一个新的Date对象。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值