您的位置:首页 > 移动开发 > Objective-C

精通JSON (JavaScript Object Notation)

2009-10-29 12:04 471 查看

3413个读者

小鼠 @ yeeyan.com 2008年11月13日

双语对照 原文

字体大小

简介
非常详细地介绍了JSON的概念,使用方式,在AJAX中的应用,最佳实践等内容

译言联合3G门户推出无线官网,手机看译言,登录wap.yeeyan.com

JSON被公认为浏览器中XML的后继者,它的目标仅仅是成为一种简单、优雅的数据格式,以方便浏览器和服务器之间的数据交换。在完成这一简单任务的过程中,它将引领下一代万维网。

对象简介

看,这就是一个对象:

var myFirstObject ={};

尽管看起来挺简单,然而那些花括号却能够记录人类所搜集的信息的每个比特,或者是表示出计算机科学家们能构思到的最复杂的程序.事实上,Javascript本身就是存储在那样一个花括号集合中的,包括它所有的基本类型 -- 字符串,数字,数组,日期,正则表达式,它们都是对象,都是像上面myFirstObject那样开始的.

创建一个新对象

旧的方式是使用"new"关键字创建新对象.

var myJSON =newObject();


这种方式已经过时,现在流行的方式是通过一对大括号定义一个空对象...

var myJSON ={};


对象即数据

在Javascript对象的最底层是一种很灵活且健壮的数据格式,表示为“名/值对”。也就是说,一个对象的属性名--可以看作是依附在对象名上的一个普通变量.并且这个对象保存着上述名字的值.看下面的例子...

var myFirstJSON ={"firstName":"John",
"lastName"  :"Doe",
"age"       :23};

document.writeln(myFirstJSON.firstName);  // 输出John
document.writeln(myFirstJSON.lastName);   // 输出Doe
document.writeln(myFirstJSON.age);        // 输出23

这个对象有3个属性或者说名/值对.在这个例子中,firstName,lastName,还有age这些名字是字符串类型.而值可以是任何的 javascript对象(请记住javascript中一切介对象,所以这个值可以是字符串,数字,数组,函数,还有其它对象类型) -- 在这个例子中,我们的值是John,Doe,还有23.John与Doe是字符串但age是数字,如你所见,这都不是问题.
这种数据格式称为JSON,JSON是JavaScript Object Notation的缩写.它能如此强大的原因是它的值可以是任何数据类型,你可以保存不同的数组与对象,按你的需要去尽情嵌套.下面是一个稍等复杂点的JSON结构...

var employees ={"accounting":[   // accounting is an array in employees.
{"firstName":"John",  // First element
"lastName"  :"Doe",
"age"       :23},

{"firstName":"Mary",  // Second Element
"lastName"  :"Smith",
"age"       :32}
],// End "accounting" array.
"sales"       :[// Sales is another array in employees.
{"firstName":"Sally",// First Element
"lastName"  :"Green",
"age"       :27},

{"firstName":"Jim",   // Second Element
"lastName"  :"Galley",
"age"       :41}
]// End "sales" Array.
}// End Employees

这里的employees是一个对象.它有两个属性或者说名值对.Accounting是一个拥有两个JSON对象的数组,分别表示了两个雇员的名字与年龄信 息.同样地,sales也是一个拥有两个JSON对象的数组,分别表示了两名工作在销售部门员工的名字与年龄.所有这些数都保存在employees对象 中.访问这些数据有几种不同的方式.

访问JSON中的数据

最常见的访问JSON数据的方式就是通过点标记.这仅仅在对象名字后面跟一个句点与要访问的属性名.如果你的对象中包含另一个对象,那只要再添加上点与对象名就可以了...

var myObject ={'color':'blue',
'animal':{'dog':'friendly'}
};

document.writeln(myObject.animal.dog);// 输出friendly


以上面的"employee"为例,如果我们想访问在"sales"工作的第一个员工信息...

document.writeln(employees.sales[0].firstName +' '+ employees.sales[0].lastName);


我们也可以访问“accounting”中的第二个员工信息.

document.writeln(employees.accounting[1].firstName +' '+ employees.accounting[1].lastName);


总 的来说,这个的例子的"employee"就是一个拥有两个数组的对象,每个数组中分别拥有两个附加对象.这种结构的唯一限制就是存储的大小和可用内存. 因为JSON可以保存一切对象,包括深层嵌套的对象,所以它的保存内容无任何限制.如果有足够的内存及储存条件,一个简单的JSON数据结构可以存储并正 确地索引迄今人类产生的所有信息.

像关联数组一样访问对象

你也可以把JSON当作一个关联数组一样访问数据.

var myFirstJSON ={"firstName":"John",
"lastName"  :"Doe",
"age"       :23};

document.writeln(myFirstJSON["firstName"]);  // 输出John
document.writeln(myFirstJSON["lastName"]);   // 输出Doe
document.writeln(myFirstJSON["age"]);        // 输出23

要 注意的是,上面的例子虽然看起来像关联数组,实质上并不是.当你遍历整myFirstObject对象的时候,除了上面三个属性之外,你还会得到分配到这 个对象的其它方法与属性,所以,当你使用上面例子的方式定位JSON数据时,要把它当作是一个对象来看,而不是关联数组.

通过AJAX接收JSON数据

通过AJAX接收JSON数据有三个不同方式.委派,回调与解释.

通过委派得到JSON

这个方法没有标准命名约定,不过"委派法"倒是一个挺好的描述名字,因为服务器创建的javascript表达式文件会把JSON分派到一个变量 中.当把服务器的返回文本作为参数传给eval函数时,someVar变量就会装载JSON对象,然后你就可以通过这个变量访问.

varJSONFile="someVar = { 'color' : 'blue' }";  // example of what is received from the server.服务器返回数据示例
eval(JSONFile);// Execute the javascript code contained in JSONFile.执行JSONFile中的javascript代码.

document.writeln(someVar.color);// 输出'blue'


通过回调得到JSON

第二个方法预先定义一个以JSON数据作为参数的函数,然后服务器返回的javascript表达式中调用这个函数.这个方法叫"回调法".这个方式被广泛地应用在处理第三方JSON数据中(例如,从其它域名获取的JSON数据)

function processData(incommingJSON){
document.writeln(incommingJSON.color);// 输出'blue'
}

// example of what is received from the server...
varJSONFile="processData( { 'color' : 'blue' } )";

eval(JSONFile);


通过解析获取JSON

最后这个方法通过一个解析函数解析原始对象文本取得JSON.这可称为"解析法".这是迄今为止最安全的传输JSON数据的方式,并且这个方式将成为下一版javascript(预计在2008年发布)的一部分.目前,非常不幸地,你在你能控制的范围内使用.

// The following block implements the string.parseJSON method 下面代码块实现了string.parseJSON方法
(function(s){
// This prototype has been released into the Public Domain, 2007-03-20
// Original Authorship: Douglas Crockford
// Originating Website: http://www.JSON.org // Originating URL    : http://www.JSON.org/JSON.js 
// 增强String原型.我们利用这个即时执行的匿名函数避免使用全局变量.
// m是转义字符表

var m ={
'/b':'//b',
'/t':'//t',
'/n':'//n',
'/f':'//f',
'/r':'//r',
'"':'//"',
'//':'////'
};

s.parseJSON =function(filter){

// 解析分三步进行,第一步,我们先用正则表达式过滤非JSON字符.我们会特别关注"()"与"new",因为它们
// 会触发调用,还有"=",它会导致赋值从而发生变化.为了安全,我们会丢弃所有不期待的字符.

try{
if(/^("(//.|[^"///n/r])*?"|[,:{}/[/]0-9./-+Eaeflnr-u /n/r/t])+?$/.
test(this)) {

// 在第二步,我们使用eval函数把文本编译到一个JavaScript结构中去.在javascript中"{"操作符容易导致语法混淆:它可以是一个代码块的开始或者一个对象直接量.我们在外层包装上括号避免混淆.

var j = eval('(' + this + ')');

// 第三步是可选的,我们递归遍历这个新结构,把每个名/值对传递到一个过滤函数,以便做一些转换.

if (typeof filter === 'function') {

function walk(k, v) {
if (v && typeof v === 'object') {
for (var i in v) {
if (v.hasOwnProperty(i)) {
v[i] = walk(i, v[i]);
}
}
}
return filter(k, v);
}

j = walk('', j);
}
return j;
}
} catch (e) {

// Fall through if the regexp test fails.

}
throw new SyntaxError("parseJSON");
};
}
) (String.prototype);
// End public domain parseJSON block

// begin sample code (still public domain tho)
JSONData = '{"color" : "green"}';   // Example of what is received from the server.
testObject=JSONData.parseJSON();
document.writeln(testObject.color); // Outputs: Green.

正 如你看到的,你需要在解析JSON数据的地方引入上面的原型方法,一旦你引入了,处理JSON数据就变得非常简单,就像上面例子最后所示,只需三行代码. 在上述三个方法中,"解析法"是最安全并且使你的代码暴露最少.你应该在用到JSON的AJAX应用中尽可能使用此方法.

通过AJAX检索JSON数据

下面的例子会使用这篇文章 介绍的AJAX框架,参考: The Ultimate Ajax Object.

当你需要与服务器交互时,从服务器到浏览器,AJAX是个较好的数据传输方式.假若服务器是在你控制范围内,使用这个方法传输是绝对安全的.下面的 例子演示了通过一个简单的AJAX请求与服务通讯,获取一些数据,并传递给一个处理函数.在这里我们将使用回调法,当JSON数据加载完后,它会被一个预 定义的函数processData(JSONData)执行处理.

function processData(JSONData){
alert(JSONData.color);
}

var ajaxRequest =new ajaxObject('http://www.somedomain.com/getdata.php');
ajaxRequest.callback =function(responseText){
eval(responseText);
}
ajaxRequest.update();

// In this example we assume the server sends back the following data file
// (which the ajax routine places in responseText)
//
// processData( { "color" : "green" } )


我们这个例子中的数据文件,实际上是一些javascript代码,当它被传递到eval语句中时会执行processData函数,把实际的JSON数据作为参数传递给这个函数.下一个例子中,我们会使用解析法,请确保你代码的其它地方有parseJSON的原型定义.

function processData(JSONData){
alert(JSONData.color);
}

var ajaxRequest =new ajaxObject('http://www.somedomain.com/getdata.php');
ajaxRequest.callback =function(responseText){
JSONData= responseText.parseJSON();
processData(JSONData);
}
ajaxRequest.update();

// In this example we assume the server sends back the following data file
// (which the ajax routine places in responseText)
//
// { "color" : "green" }

现在,当服务返回JSON文件时,它会被“JSONData = responseText.parseJSON();”这段代码解析并且把结果JSONData传递给处理函数processData.得到的结果与第一个例子一样,不过,由于某些原因,当JSON数据中含有恶意代码或非法数据时,使用第二个方法会更安全,而且便于处理各种问题.

如何发送JSON数据到服务器

由于AJAX把数据当作编码好的字符串发送,这样在发送到服务器前就需要对JSON数据做一些准备工作.非常幸运,Douglas Crockford已经在JSON.org发布了一些非常有用的通用代码,可以把javascript数据类型转换成便于发送到服务器的JSON字符串.

这个库的源代码可能可以通过http://www.JSON.org/JSON.js获取.这些代码的使用是没有任何约束的,你可以复制粘贴到你的工具箱中.

下面这个例子定义了一个JSON对象,然后使用toJSONString()方法把这个对象转换成准备发送到服务器的字符串.

var employees ={"accounting":[   // accounting is an array in employees.
{"firstName":"John",  // First element
"lastName"  :"Doe",
"age"       :23},

{"firstName":"Mary",  // Second Element
"lastName"  :"Smith",
"age"       :32}
],// End "accounting" array.
"sales"       :[// Sales is another array in employees.
{"firstName":"Sally",// First Element
"lastName"  :"Green",
"age"       :27},

{"firstName":"Jim",// Second Element
"lastName"  :"Galley",
"age"       :41}
]// End "sales" Array.
}// End Employees

var toServer = employees.toJSONString();

document.writeln(toServer);

输出:

//{"accounting":[{"firstName":"John","lastName":"Doe","age":23},{"firstName":"Mary","lastName":"Smith","age":32}],"sales":[{"firstName":"Sally","lastName":"Green","age":27},{"firstName":"Jim","lastName":"Galley","age":41}]}


从第三方服务获取JSON

本节将看到啰嗦的忠告.到目前为止,JSON与AJAX相对来说还是安全的,因为交互的服务与获取的数据都在你的控制范围内.但对于第三方服务来说,这一切都改变了.
在IE7与Firefox2中,使用第三方的JSON数据将会使你的网页面临恶意攻击及重大的安全风险.当你请求第三方的数据时,你就相当于失去了页面的控制权,而第三方可以为所欲为地从页面搜刮数据并把敏感信息回发,然后再安装个监听器等待敏感数据.
可以确定的是,请求外部的JSON你唯一得到的就是一堆有用的数据.然而,如果页面上包含表单元素,请求安全/加密链接,或者包含其它任何个人隐私数据的,绝不能在此页面上使用第三方的JSON调用.
当你尝试使用第三方的JSON数据去美化,充实你创建的页面时,你想象一下现在世界上最坏的人正在和你一起看着这个页面.如果其他人可以使相关信息显示在这个页面上让你觉得不舒服,那就不要使用第三方JSON调用.
除了RSS/XML之外,不少服务开始提供JSON这种数据格式.特别是Yahoo,是实施JSON的先驱.JSON最酷的地方就是页面可以直接请求并处 理它的数据,不同于XML,得先向服务器请求再传递给浏览器.但非常不幸,现在还没有JSON的RSS/FEED标准,虽然很多服务都模拟了JSON结构 的RSS XML.

下面的例子中,我们会使用本站的JSON feed,非常简单的XML到JSON一对一转换.这个FEED甚至与生成XML版本的是同一个程序,只是url传递的参与去决定选择哪种格式.下面是本站FEED的链接:

http://www.hunlock.com/feed.php?format=JSON&callback=JSONFeed


所有第三方JSON要求使用回调方法的地方,当JSON数据文件被加载完成后都会调用一个特别函数,并把JSON数据作为这个函数的第一个参数.很 多服务都有一个默认的调用函数,而一些服务能让你自己指定这个调用函数的名字.我的JSON feed默认会调用 JSONFeed(),但你可以通过URL去改变这个函数名. 假如你改变读取的URL如下:

http://www.hunlock.com/feed.php?format=JSON&callback=processJSON

现在,当Feed被加载完成后,它会调用"processJSON"函数,并把JSON数据作为首个参数传递进去.这个对你需要处理多个JSON请求的时候尤为重要.

所有第三方JSON请求都通过<script>标签加载,就像平时我们引入外部的javascript文件一样.有点不同的是我们要手 工创建script标签并手工加到页面中去.这是一个相当简单的处理,而且代码也简单易懂.下面这个小函数接受一个链接并加载之.

function loadJSON(url){
var headID = document.getElementsByTagName("head")[0];
var newScript = document.createElement('script');
newScript.type ='text/javascript';
newScript.src = url;
headID.appendChild(newScript);
}

这个函数先取得页面上首个<head>元素,然后创建一个新的script元素,设置脚本类型,并把传递进来的url设置为src属性,最后把这个script元素添加到<head>元素中,一旦添加完成,这个脚本就会马止被加载并执行.

下面这个小程序会获取本站的JSON feed然后显示新闻条目.

function loadJSON(url){
var headID = document.getElementsByTagName("head")[0];
var newScript = document.createElement('script');
newScript.type ='text/javascript';
newScript.src = url;
headID.appendChild(newScript);
}

function processJSON(feed){
document.writeln(feed.title+'<BR>');
for(i=0; i<feed.channel.items.length; i++){
document.write("<a href='"+feed.channel.items[i].link+"'>");
document.writeln(feed.channel.items[i].title+"</a><BR>");
}
}

loadJSON('http://www.hunlock.com/feed.php?format=JSON&callback=processJSON');

这段代码创建了一个加载第三方javascripts的函数(loadJSON)和一个处理返回数据的函数(processJSON).最后一行调用了 loadJSON函数并把链接传递给它.当脚本完成加载后,它就会自动执行调用processJSON函数(因为我们已经在回调中指定了这个函数名),然 后processJSON将循环文件中所有新闻项把文章标题作为可点击的链接显示出来.

Yahoo管道

绝大部分的网站仍旧使用XML进行聚合内容,这意味着你需要一个服务去对这些数据做些处理.Yahoo管道可以为你把任务RSS/XML feed转换成JSON格式.转换很简单,你只要把你需使用的RSS feed链接添加到下面这行的末尾...

http://pipes.yahoo.com/pipes/9oyONQzA2xGOkM4FqGIyXQ/run?&_render=JSON&_callback=piper&feed=

这就是一个yahoo管道,可以接受RSS/XML feed并转换成JSON格式.使用这个小工具你的页面可以直接处理网络上任何XML/RSS数据而不需要服务器端程序.

修改上面的例子,读取dzone的RSS feed:

function loadJSON(url){
var yahooPipe ='http://pipes.yahoo.com/pipes/9oyONQzA2xGOkM4FqGIyXQ/run?&_render=JSON&_callback=processJSON&feed=';
var headID = document.getElementsByTagName("head")[0];
var newScript = document.createElement('script');
newScript.type ='text/javascript';
newScript.src = yahooPipe+url;
headID.appendChild(newScript);
}

function processJSON(feed){
document.writeln(feed.value.title+'<BR>');
for(i=0; i<feed.value.items.length; i++){
document.write("<a href='"+feed.value.items[i].link+"'>");
document.writeln(feed.value.items[i].title+"</a><BR>");
}
}

loadJSON('http://feeds.dzone.com/dzone/frontpage');

想了解更多如何使用yahoo管道的信息,可以访问这个文章:Yahoo Pipes--RSS without Server Side Scripts.

JSON的安全

很多让人歇斯底里的JSON安全问题会带来非常怪异的事情.例如这样一幕:假如能诱骗一个用户去访问这样一个页面,这个页面会启动第三方的脚本对安 全站点发起JSON请求并嗅探收集敏感信息.这种攻击企图欺骗服务器认为用户只是在它的站点里请求JSON数据.假如被攻击的服务器只是以回调或委派的方 式提供JSON服务--服务器只是返回需被解析的原生JSON数据文件,那这样的攻击是无效的,因为<script>标签不能触发解析,只有 AJAX可以解析返回文本,并且AJAX不跨域使用.

这就是本文(和许多安全专家)推荐你对敏感JSON数据使用必需经过解析的原生JSON文件的原因.

当然,不厌其烦地再次提醒你,如果你的页面无论何时都包含敏感信息的话,你最好不要去使用<script>标签加载第三方JSON数据.

这是一份非常好的JSON安全漏洞概述文档: JavaScript Hijacking.pdf

还有概述了巧妙地通过原型攻击AJAX安全漏洞的文档: Subverting Ajax.pdf

JSON最佳实践

根据上述的安全报告,Douglas Crockford (Yahoo公司的javascript高级设计师)写了一篇blog,里面提供了非常简单而优雅的方法避免最常见的JSON安全问题.这些方法总结如下:

浏览器永远都是不可信任的

保持数据简洁(不要在JSON数据中嵌入函数)

避免使用第三方JSON

所有JSON数据请求服务器端验证(保证它们都属于你的站点)

对敏感信息使用SSL(浏览器加密)

JSON的未来

现在JSON正处于起步阶段,然而它的前景是非常广阔的.PHP已经在第5个版本支持JSON.而XML在RSS的统治地位也面临分裂.

到2008年,大多数浏览器将能解析JSON并且能把任何变量转换成JSON.还有一个即将实现的标准就是允许浏览器安全地请求一个当前域名之外的JSON 文件.当这些技术都被实现时,我们将进入一个Web3.0的世界,到时我们可以请求任何地方公开的任何JSON数据并且自由控制,而不必使用任何代理服务器.

XML在被引入的时候引起轰动,但JSON,当它被浏览器完全支持的时候,将会引发网络新风潮,届时,新基于浏览器支持JSON的应用风行,如果你还没为那一天作为准备,你将会在下次变革中失去领先地位.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: