企业级项目实战(带源码)地址:http://zz563143188.iteye.com/blog/1825168
JSON入门
JSON(JavaScript Object Notation http://www.json .org/json -zh.html ),是一种轻量级的基于文本且独立于语言的数据交换格式,比XML更轻巧,它是XML数据交换的一个替代方案。它源于ECMAScript程序语言标准-第3版(ECMA-262 3rd Edition - December 1999)的子集,定义了便于表示结构化数据的一套格式规范,JSON规范是符合ECMAScript语法规范,这样按JSON规范描述出的字符串已是 JavaScript的原生代码串,这使之能通过eval动态的在JSON串与JavaScript对象之间进行转换。如果夸大来说,它是另一种理想的但有别于XML数据交换语言。
“名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。
对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。
数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。
值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。这些结构可以嵌套。
字符串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。与C或者Java的字符串非常相似。
假如有一个employee对象,它有“姓、名、员工编号、头衔”等信息,先看看JSON是如何来描述它的:
- {
- employee :
- {
- firstName: "John",
- lastName : "Doe",
- employeeNumber : 123,
- title : "Accountant"
- }
- }
再来看看XML是如何表示的,请看:
- <employee>
- <firstName>John</firstName>
- <lastName>Doe</lastName>
- <employeeNumber>123</employeeNumber>
- <title>Accountant</title>
- </employee>
从上面描述看,JSON表示法不正是JavaScript中对象描述的一种方式么?正确,这正是JavaScript中的对象构造的原生代码。既然是原生代码,我们把它转换成JavaScript中的对象,这样我们操作对象就比操作字符串方便多了。
把JSON字符串转换成JavaScript对象:
- <script type="text/javascript">
- //使用script本身的函数eval将JSON串解析成对象
- var e = eval(
- '({' +
- 'employee : ' +
- '{' +
- 'firstName: "John",' +
- 'lastName : "Doe",' +
- 'employeeNumber : 123,' +
- 'title : "Accountant"' +
- '}' +
- '})'
- );
- //现在我们可以使用e这个对象了,还可以以点的访问形式来访问对象的属性
- alert(e.employee.firstName);
- alert(e.employee.lastName);
- alert(e.employee.employeeNumber);
- alert(e.employee.title);
- </script>
看完上述后我们来对比它们。
经过一番快速浏览后如何?感觉到没有JSON的设计上比XML更轻巧简洁?先前就说过了,正是它符合JavaScript语言对象本身特点,这使得如果服务器传来的文本是符合JavaScript语法定义语句的字符串,那岂不是一条eval方法就能解析了?的确如此~
从上面两者的表示来看,JSON表示法在语法上要比XML要简洁的多,由于不需要使用关闭标签来呼应开始标签,因此许多多余的信息不再出现了,相对XML而言基本上不存在数据冗余,这在传输与响应速度上大在提高了。
另外,JSON不只是在表现形式上有如此的优势,最重要的是可以丢弃以前弄得我们晕头转向的DOM解析了(客户端的JavaScript的XML DOM解析,服务器端的DOM、SAX、Dom4j、Jdom等)。JSON与XML相比对JavaScript有着更好的通用性,一段JSON格式数据经过JavaScript一个简单的方法(eval)即可转换成 JavaScript对象供程序调用,转换方法是浏览器的JavaScript内部定义好的无需再手工编写。而一段XML格式的数据需要调用浏览器内部的 XML解析器工具进行解析后才可以使用。而对于不同内核的浏览器(如IE、Netscape等)XML解析的方法是有差别的,因此需要针对不同浏览器内核做不同的方法封装,从而给客户端开发带来一定的复杂度。相比之下JSON被浏览器解析的速度更快。在服务器端不同的语言也有不同的JSON解析器,可以很方便的解析客户端传过来的字符串,而不像为了读取XML还是借助于这样或那样的API工具。
优点:
乍看上去,使用JSON的数据分隔符的优点可能并不那么明显,但存在一个根本性的缘由:它们简化了数据访问。使用这些数据分隔符时, JavaScript引擎对数据结构(如字符串、数组、对象)的内部表示恰好与这些符号相同。
JSON的另一个优点是它的非冗长性。在XML中,打开和关闭标记是必需的,这样才能满足标记的依从性;而在JSON中,所有这些要求只需通过一个简单的括号即可满足。在包含有数以百计字段的数据交换中,传统的XML标记将会延长数据交换时间
此外,JSON受到了擅长不同编程语言的开发人员的青睐。这是因为无论在Haskell中或 Lisp中,还是在更为主流的C#和Java中,开发都可以方便地生成JSON。
不足:
和许多好东西都具有两面性一样,JSON的非冗长性也不例外,为此JSON丢失了XML具有的一些特性。命名空间允许不同上下文中的相同的信息段彼此混合,然而,显然在JSON中已经找不到了命名空间。JSON与XML的另一个差别是属性的差异,由于JSON采用冒号赋值,这将导致当XML转化为 JSON时,在标识符(XML CDATA)与实际属性值之间很难区分谁应该被当作文本考虑。
另外,JSON片段的创建和验证过程比一般的XML稍显复杂。从这一点来看,XML在开发工具方面领先于JSON。
JSON实践
预备知识
在进一步学习之前,我们有必要讲解一下eval方法的用法,懂的同学可以跳过。
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。它接收一个参数s,如果s不是字符串,则直接返回s。否则执行s语句。如果s语句执行结果是一个值,则直接返回此值,否则返回undefined。
另外,该方法只接受原始字符串作为参数,如果 string 参数不是原始字符串,那么该方法将不作任何改变地返回。因此请不要为 eval() 函数传递 String 对象来作为参数:
- var str = new String("alert('msg')");
- //请不要传递String对象,否则直接返回string对象了
- alert(eval(str)==str);//true
- //应该传递原始string字符串,这样才看作JavaScript脚本并执行
- eval("alert('msg')");//msg
最后,需要特别注意的是对象声明语法“{}”并不能返回一个值,需要用括号括起来才会返回值(括号里的脚本是表达式,有返回值,而不是无返回值的逻辑式,因为大括号里的脚本又可能是表达式,又可能是普通的逻辑表达,所以用小括号括起来后明确的说明是值表达式):
- var str="{}";//花括号内没属性时
- alert(eval('(' + str + ')'));//弹出:[object Object]
- alert(eval(str));//弹出:undefined
- str="{name:'jzj'}";//花括号内有一个属性
- alert(typeof eval('(' + str + ')'));//弹出:object,以对象方式返回
- alert(eval(str));//弹出:jzj
- alert(eval('(' + str + ')').name);//弹出:jzj
- alert(eval(str).name);//弹出:undefined
- str="{name:'jzj',age:30}";//花括号内有多个属性时
- alert(eval('(' + str + ')'));//
- alert(eval('(' + str + ')').name);//jzj
- alert(eval('(' + str + ')').age);//30
- //alert(eval(str));//运行时会出错,多个属性时不用小括号运行出错
可以看到,对于对象声明语句来说,仅仅是执行,并不能返回值 。为了返回常用的“{}”这样的对象声明语句,必须用括号括住,以将其转换为表达式,才能返回其值 。这也是使用JSON来进行Ajax开发的基本原理之一。
现来说说本节的重点,就是在应用eval时,动态脚本所生成的变量都局部的,但很多时候我们可能在调用eval函数的外面使用生成的变量,eval不可能在全局空间内执行,这就给开发带来了不少问题,这该如何作?请看继续往下看吧。
我们先来证实一下eval产生的变量是局部性的,在调用eval函数外是不能访问到此变量的。
- var str='全局';//定义一个全局变量
- function test(){
- eval('var str="局部"');
- alert(str);//局部
- }
- test();
- alert(str); //弹出:全局
另外,eval生成的函数也是局部的,它只能在生成它的函数内使用,出函数域就不能调用的到。
解决办法:
- function gloablEval(code){
- if(window.attachEvent && !window.opera){
- //ie
- execScript(code);
- }else{
- //not ie window对象不能省,否则生成的变量在执行的函数外不可见
- window.eval(code);
- alert(str);//局部
- //eval(code);
- //alert(str);//局部
- }
- }
- var str='全局';//定义一个全局变量
- gloablEval('var str="局部"');
- alert(str); //弹出:局部,这里用局部的履盖了全局变量的值
现解释一下:
1、对于IE浏览器,默认已经提供了这样的函数:execScript,用于在全局空间执行代码。
2、对于Firefox浏览器,直接调用eval函数,则在调用者的空间执行;如果调用window.eval则在全局空间执行。
我们知道,可以使用eval()方法调用JavaScript的编译器把JSON文本转变成对象。因为JSON是JavaScript的一个确切的子集,编译器可以正确地解析JSON文本,然后生成一个对象结构。
- var myObject = eval('(' + myJSONtext + ')');
eval函数非常快速。它可以编译执行任何JavaScript程序,因此产生了安全性问题。当使用可信任与完善的源代码时才可以使用eval函数。这样可以更安全的使用JSON解析器。使用XMLHttpRequest的web应用,页面之间的通讯只允许同源,因此是可以信任的。但这却不是完善的。如果服务器没有严谨的JSON编码,或者没有严格的输入验证,那么可能传送包括危险脚本的无效JSON文本。eval函数将执行恶意的脚本。
如果关心安全的话,使用JSON解析器可以防止此类事件。JSON解析器只能辨识JSON文本,拒绝所有脚本,因此它比较安全,JSON官方网站提供的一个开源的JSON解析器和字符串转换器(http://www.json.org/json.js )。
- var myObject = myJSONtext.parseJSON();
而JSON的字符串转换器(stringifier)则作相反的工作,它将JavaScript数据结构转换为JSON文本。JSON是不支持循环数据结构的,所以注意不能把循环的结构交给字符串转换器。
- var myJSONText = myObject.toJSONString();
Java中的JSON解释器官方提供了好几种,我们这里使用的是org.json 包,关于如何使用,请参见另一篇《JSON之org.json包测试》 ,它是基于官方包提供的测试改写而来的。
开始实战
本实例实现了客户端与服务器端通过JSON进行参数的传递与接收,而不是通过原来的XML方式进行通信。页面采用了Prototype的Ajax方式进行异步通信,并采用了官方json.js 进行对象与JSON串的灵活互转。服务器端采用了官方提供org.json 包进行JSON串与Java对象的互转。具体的细节请看代码注释。
客户端实现:
- <html>
- <head>
- <title> JSON Address Book </title>
- </head>
- <body>
- <div style="text-align:left" id="addressBookResults"></div>
- <script type="text/javascript" src="prototype-1.4.0.js"></script>
- <script type="text/javascript" src="json.js"></script>
- <script type="text/javascript">
- //address对象
- function address(city,street,zip){
- this.city = city;//城市
- this.street = street;//街道
- this.zip = zip;//邮编
- }
- //addressbook对象
- function addressbook(city,street,zip,name,tel1,tel2){
- //addressbook对象中含有address对象属性
- this.address = new address(city,street,zip);
- //人的名字属性
- this.name = name;
- //人的电话号码属性,且有两个电话号码
- this.phoneNumbers = [tel1,tel2];
- }
- //创建两个addressbook对象实例,这些信在实际的项目中是由用户通过页面输入的
- var addressbookObj1 = new addressbook("Seattle, WA","P.O BOX 54534",
- 42452,"Ann Michaels",
- "561-832-3180","531-133-9098");
- var addressbookObj2 = new addressbook("Miami, FL","53 Mullholand Drive",
- 72452,"Betty Carter",
- "541-322-1723","546-338-1100");
- //创建要传递给后台的参数对象
- var paramObj={};
- //因为有多个(这里是两个),我们用数组的形式
- paramObj.addressbook=new Array(addressbookObj1,addressbookObj2);
- //通过对象实例的toJSONString方法,JavaScript对象转JSON串
- var param = paramObj.toJSONString();
- //alert(param);
- // 定义 service URL
- var url = '/json_addressbook/addrbk?timeStamp='+new Date();
- // 通过原型创建AJAX请求的WEB服务, 响应后, 回调 showResponse 方式
- new Ajax.Request( url, { method: 'post', parameters:"jsonStr="+param,
- onComplete: callBack });
- // 回调方法,接收服务器返回过来的JSON串,
- //并用eval函数或String对象实例parseJSON转换成JavaScript对象
- function callBack(originalRequest) {
- // 获取服务器返回的JSON原始串
- var jsonRaw = originalRequest.responseText;
- //原始串转换成JavaScript对象
- //var jsonRawObj = eval("(" + jsonRaw + ")");用此种方式也行
- var jsonRawObj = jsonRaw.parseJSON();
- //从json原始对象中提取HTML格式串
- var jsonHtmlStr = jsonRawObj.jsonHtmlStr;
- //提取AddreeBook的JSON串
- var jsonCode = jsonRawObj.jsonCode;
- // 通过eval动态的把JSON响应串构造成JavaScript对象
- //var jsonContent = jsonCode.parseJSON();用此种方式也行
- jsonContent = eval("(" + jsonCode + ")");
- // 从服务器获取的JSON格式串后,显示数据到页面
- finalResponse = "<b>服务器返回的JSON串如下: </b><br/><br>";
- finalResponse += jsonHtmlStr+"<br/>";
- finalResponse += "<br><b>从JSON对象提取信息如下: </b><br/><br>";
- // 根据地址薄长度循环.
- for (i = 0; i < jsonContent.addressbook.length; i++) {
- finalResponse += "<hr/>";
- finalResponse += "<i>Name:</i> " + jsonContent.addressbook[i].name + "<br/>";
- finalResponse += "<i>Address:</i> " + jsonContent.addressbook[i].address.street
- + " -- " +
- jsonContent.addressbook[i].address.city[0] + "," +
- jsonContent.addressbook[i].address.zip + ".<br/>";
- finalResponse += "<i>Telephone numbers:</i> "
- + jsonContent.addressbook[i].phoneNumbers[0] + " & " +
- jsonContent.addressbook[i].phoneNumbers[1] + ".";
- }
- // 把结果置换到结果区域并显示
- document.getElementById("addressBookResults").innerHTML = finalResponse;
- }
- </script>
- </body>
- </html>
页面展示结果:
JSON Address Book
服务器返回的JSON串如下:
{"addressbook": [ { "address": { "city": [ "Seattle, WA", "changsha" ], "street": ["P.O BOX 54534"], "zip": [42452] }, "name": "Ann Michaels", "phoneNumbers": [ "561-832-3180", "531-133-9098" ] }, { "address": { "city": [ "Miami, FL", "changsha" ], "street": ["53 Mullholand Drive"], "zip": [72452] }, "name": "Betty Carter", "phoneNumbers": [ "541-322-1723", "546-338-1100" ] } ]} 从JSON对象提取信息如下: Name: Ann Michaels Address: P.O BOX 54534 -- Seattle, WA,42452. Telephone numbers: 561-832-3180 & 531-133-9098. Name: Betty Carter Address: 53 Mullholand Drive -- Miami, FL,72452. Telephone numbers: 541-322-1723 & 546-338-1100. |
服务器端实现:
- package orgjson;
- import java.io.IOException;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.SortedMap;
- import java.util.TreeMap;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.json.JSONArray;
- import org.json.JSONException;
- import org.json.JSONObject;
- import bean.Address;
- /**
- * 使用 org.josn 包进行解析操作
- * (C) 2009-9-1, jzjleo
- */
- public class AddressServlet extends HttpServlet {
- private static final long serialVersionUID = -1762985503942785440L;
- protected void service(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- // 创建addressbook数据结构
- SortedMap addressBook = new TreeMap();
- // 创建新的address实体并放置到Map中
- Address annMichaels = new Address("P.O BOX 54534", "Seattle, WA", 42452,
- "561-832-3180", "531-133-9098");
- addressBook.put("Ann Michaels", annMichaels);
- Address bettyCarter = new Address("53 Mullholand Drive", "Miami, FL", 72452,
- "541-322-1723", "546-338-1100");
- addressBook.put("Betty Carter", bettyCarter);
- try {
- // 准备转换通讯簿映射到JSON数组
- // 数组用于放置多个地址条目
- JSONArray jsonAddressBook = new JSONArray();
- // 迭代过滤地址簿条目
- for (Iterator iter = addressBook.entrySet().iterator(); iter.hasNext();) {
- // 找当前迭代项
- Map.Entry entry = (Map.Entry) iter.next();
- String key = (String) entry.getKey();
- Address addressValue = (Address) entry.getValue();
- // 以键值对的形式存入条目并分配给"name"
- JSONObject jsonResult = new JSONObject();
- jsonResult.put("name", key);
- // 获取和创造相应的地址结构,綁定到新Key
- // 追加地址条目到JSON格式结果
- String streetText = addressValue.getStreet();
- String cityText = addressValue.getCity();
- int zipText = addressValue.getZip();
- //--注:Bean可以直接转换成JSON串,下面可以采用 new JSONObject(addressValue)
- //形式来自动转换成JSON串
- JSONObject jsonAddrObj = new JSONObject();
- //以数组的形式存放,street为存放数组的变量名即Key,如果key不存在,则新建,如果存在,
- //则在原来数组后追加
- //jsonAddress.append("street", streetText);
- //上句等价于下面语句
- jsonAddrObj.put("street", new JSONArray().put(streetText));
- jsonAddrObj.append("city", cityText);
- jsonAddrObj.append("city", "changsha");//追加
- //上两句等价于如下语句
- //jsonAddrObj.put("city", new JSONArray().put(cityText).put("changsha"));
- jsonAddrObj.append("zip", new Integer(zipText));
- jsonResult.put("address", jsonAddrObj);
- // 获取和结构,建立相应的电话到新的Key
- // 追加在电话条目到JSON格式的结果里
- String telText = addressValue.getTel();
- String telTwoText = addressValue.getTelTwo();
- JSONArray jsonTelephones = new JSONArray();
- jsonTelephones.put(telText);
- jsonTelephones.put(telTwoText);
- jsonResult.put("phoneNumbers", jsonTelephones);
- // 把JSON地址条目放置到全局的JSON地址薄数组里
- jsonAddressBook.put(jsonResult);
- } // 结束循环地址薄
- // 赋值JSON地址薄到结果字符变量
- JSONObject resultJsonObj = new JSONObject().put("addressbook",
- jsonAddressBook);
- //格式化输出到页面上的JSON串
- String jsonHtmlStr = resultJsonObj.toString(4).replaceAll(" ", " ")
- .replaceAll("\n", "<br>");
- JSONObject jsonObj = new JSONObject().put("jsonHtmlStr", jsonHtmlStr).put(
- "jsonCode", resultJsonObj.toString());
- // 返回JSON串
- resp.getOutputStream().write(jsonObj.toString().getBytes());
- System.out.println(resultJsonObj.toString(4));
- //System.out.println(jsonObj.toString(4));
- System.out.println("-----------------------------------------------");
- readJSONString(req);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 从客户端读取JSON串
- * @param req
- */
- public void readJSONString(HttpServletRequest req) {
- System.out.println("接收到客户端的JSON信息如下:");
- JSONObject clientJSONObj;
- try {
- clientJSONObj = new JSONObject(req.getParameter("jsonStr"));
- System.out.println(clientJSONObj.toString(4));
- JSONArray addressbookArray = clientJSONObj.getJSONArray("addressbook");
- for (int i = 0; i < addressbookArray.length(); i++) {
- System.out.println("The" + " " + (i + 1) + " addressbook msg:");
- JSONObject addressbookJSONObj = addressbookArray.getJSONObject(i);
- JSONObject addressJSONObj = addressbookJSONObj.getJSONObject("address");
- System.out.println("address-------");
- System.out.println(" " + addressJSONObj.getString("city"));
- System.out.println(" " + addressJSONObj.getString("street"));
- System.out.println(" " + addressJSONObj.getString("zip"));
- System.out.println("name----------");
- System.out.println(" " + addressbookJSONObj.getString("name"));
- System.out.println("phoneNumbers--");
- JSONArray phoneNumbersArr = addressbookJSONObj
- .getJSONArray("phoneNumbers");
- System.out.println(" " + phoneNumbersArr.getString(0));
- System.out.println(" " + phoneNumbersArr.getString(1));
- System.out.println();
- }
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- }
服务器后台打印:
{"addressbook": [ { "address": { "city": [ "Seattle, WA", "changsha" ], "street": ["P.O BOX 54534"], "zip": [42452] }, "name": "Ann Michaels", "phoneNumbers": [ "561-832-3180", "531-133-9098" ] }, { "address": { "city": [ "Miami, FL", "changsha" ], "street": ["53 Mullholand Drive"], "zip": [72452] }, "name": "Betty Carter", "phoneNumbers": [ "541-322-1723", "546-338-1100" ] } ]} ----------------------------------------------- 接收到客户端的JSON信息如下: {"addressbook": [ { "address": { "city": "Seattle, WA", "street": "P.O BOX 54534", "zip": 42452 }, "name": "Ann Michaels", "phoneNumbers": [ "561-832-3180", "531-133-9098" ] }, { "address": { "city": "Miami, FL", "street": "53 Mullholand Drive", "zip": 72452 }, "name": "Betty Carter", "phoneNumbers": [ "541-322-1723", "546-338-1100" ] } ]} The 1 addressbook msg: address------- Seattle, WA P.O BOX 54534 42452 name---------- Ann Michaels phoneNumbers-- 561-832-3180 531-133-9098 The 2 addressbook msg: address------- Miami, FL 53 Mullholand Drive 72452 name---------- Betty Carter phoneNumbers-- 541-322-1723 546-338-1100 |