json转换 eval问题

什么是JSON?

大多数开发者不是只进行AJAX程序程序开发的,我这里先介绍一点背景知识。JSON是一种简单的、人能够阅读的数据交换格式,在AJAX程序中,当服务器与web程序之间传输数据时,通常采用这种格式。

举例来说,假如你从收藏的web邮件中选择一个联系人名称,以便能够看到该联系人信息。服务器向web程序(运行在浏览器中)发送的数据流可能是下面的样子:

{

"firstName":"cyra",

"lastName":"richardson",

"address":{

"streetAddress":"1Microsoftway",

"city":"Redmond",

"state":"WA",

"postalCode":98052

},

"phoneNumbers":[

"425-777-7777",

"206-777-7777"

]

}

值得庆幸的是,这种格式与JavaScript的语法完全兼容。当今的很多程序使用Javascript的eval()函数将这种得到的数据转换成Javascript对象。使用eval()是不安全的,并且耗费资源。eval()将这个字符串解析为Jscript表达式,并且执行。如果传递给eval()的字符串被篡改过,它就可能含有我们不期望的数据,甚至是别人的代码,这样就注入到了你的web程序中。

现在,有很多采用Javascript编写的库,用来更加安全地解析不受信任的JSON数据。有些使用Jscript编写的解析器(http://www.json.org/json_parser.js)对数据进行了严格的验证,有些库,像json2,js(http://www.json.org/json2.js),采用正则表达式对输入的字符串进行全面的检查,然后使用eval()快速解析。理想的解决方案是一种原生实现方法,避免应用程序遭受代码注入,运行很快,并且随处都能使用。

IE8Jscript中原生JSON

IE8的Jscript引擎已经有了JSON完全的原生实现,在保持与ES3.1提案草案(ProposalWorkingDraft,地址http://wiki.ecmascript.org/doku.php?id=es3.1:es3.1_proposal_working_draft)中所描述的JSON支持的兼容性的同时,极大地提高了序列化、反序列化的速度,并且提高解析不信任数据的安全性。

API

我们定义了一个新的内置对象“JSON”,这个对象可被修改或者重写。看上去很像math或者其他内置的全局对象。除了JSON对象之外,toJSON()这些特定的函数也添加到了Date、Number、String和boolean对象的原型上。JSON对象有两个方法:parse()和stringify()。

例如:

varjsObjString="{\"memberNull\":null,\"memberNum\":3,\"memberStr\":\"StringJSON\",\"memberBool\":true,\"memberObj\":{\"mnum\":1,\"mbool\":false},\"memberX\":{},\"memberArray\":[33,\"StringTst\",null,{}]";

varjsObjStringParsed=JSON.parse(jsObjString);

varjsObjStringBack=JSON.stringify(jsObjStringParsed);

这个由parse()方法产生、又通过stringify()方法序列化回去的对象与下面的对象是完全一样的:

varjsObjStringParsed=

{

"memberNull":null,

"memberNum":3,

"memberStr":"StringJSON",

"memberBool":true,

"memberObj":

{

"mnum":1,

"mbool":false

},

"memberX":{},

"memberArray":

[

33,

"StringTst",

null,

{}

]

};

JSON.parse(source,reviver)

JSON.parse方法执行反序列化,它采用JSON格式的字符串(由参数source指定),产生Jscript对象或者数组。

可选参数revive是一个用户自定义函数,用来计入解析的变化。结果对象或者数组递归遍历,reviver函数用在每一个成员上,每个成员值被reviver的返回值所替代。如果reviver返回null,则对象成员被删除。对reviver的遍历和调用是按后序遍历完成的。也就是说:对象的所有成员被“revived”之后,整个对象也就“revived”了。

reviver主要用来识别类似ISO这样的字符串,将它们转成Date对象。到目前为止,JSON格式(http://www.json.org/)对Date对象来说,是不能来回转换的,这是因为没有Jscript的标准Date文字量。ES3.1草案(http://wiki.ecmascript.org/doku.php?id=es3.1:es3.1_proposal_working_draft)包含了一个如何使用reviver函数解决这个问题的例子。

JSON.stringify(value,replacer,space)

这个是序列化方法。它以由value参数指定的对象或者数组为参数,生成JSON格式的字符串。对象或者数组递归访问,序列化成特定的JSON格式。如果value参数有toJSON()方法,那么这个方法就起第一个过滤器的作用,原始的value被value.toJSON(key)替代,最终的值被序列化。参数key是一个字符串,当类似(key:value)这样的对象被序列化时,key是成员的名字。对根对象来说,key是空字符串。

Date.prototype.toJSON()生成一个无需转义的字符串,是真正的序列化器,因为stringify()会返回最原始、没有任何变化的字符串。Date对象通过toJSON()方法进行序列化。

Number.prototype.toJSON()、String.prototype.toJSON()、Boolean.prototype.toJSON()函数返回ValueOf()。他们用来进行对象的正确序列化,像“varnum=newNumber(3.14);”这样的对象。

可选的replacer参数起过滤器的作用,递归使用。它可以是个函数,也可以是个数组。如果replacer是一个函数,那么对每个对象成员key:value都调用replacer(key,value)。至于根对象,调用replacer("",value)。如果replacer是个数组,则必须是个数组字符串。数组的元素就是要进行序列化成员的名字。序列化的顺序按照数组中的名字顺序。在序列化数组时,数组replacer是被忽略的。

可选的参数space是关于如何格式化输出文字的,如果该参数省略,则输出文字没有任何额外的空格。如果它是一个数字,它指定的是每个级别缩进的空格数。如果它是一个字符(比如"\t"或者“”),它就以这些字符缩进每一个级别的字符。

对现有的网页有何影响?

ES3.1JSON提案是被流行的json2.js所使用的主要因素。我们也采用JSON这个名字。全局对象JSON能够被重写。然而,它不再是一个未定义的对象。这与通过在脚本语言中引入new关键字是相同的。采用一个名字偶尔会影响现有的代码。使用json2.js的页面不太可能会受影响。除了极少数的例外,所有这些页面都将会继续正常工作,只能是运行得更快。

那些自己实现的JSON对象定义的页面可能会受到影响,尤其是使用类似“if(!this.JSON){JSON=…}”这种模式定义的JSON对象。有两种主要的方法可以解决这个问题:

1,将现有代码迁移,使用原生JSON对象

如果自己的JSON实现是基于json2.js的某种版本的,迁移起来就很简单。

2,决定不使用原生JSON支持,继续使用自己现有的JSON对象

这可以通过重命名或者重写JSON名字实现。重命名意味着要将所有使用JSON名字的代码修改成类似“MyJSON”这样的名字。重写意味着确保自己的JSON定义重写所有使用默认原生JSON定义的代码。大多数情况下,只需移除条件“if(!this.JSON)”就可以了。

考虑到3.1标准的影响,使用JSON这个名字与我们通过定义好的接口进行互操作的愿望是一致的。

关于原生JSON,要谈论的事情还有很多。解析器不是基于eval()的,是一个独立的实现。它与JSON支持(http://wiki.ecmascript.org/doku.php?id=es3.1:json_support)提供的引用解析器是等同的。它也是和http://www.json.org/json_parser.js一样安全的,并且运行速度要快很多。所以,如果你使用eval(),或自己的JSON库,请检查一下IE8中原生JSON实现,以便得到更好的性能和更安全的操作。

详细出处参考:http://www.jb51.net/article/17721.htm

相关推荐