JSON — JSON入门与实战详解 —— JavaScript、Java与JSON互转
文章分类:Web前端
JSON入门
什么是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数据交换语言。
JSON建构于两种结构
“名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表 (hash table),有键列表(keyed list),或者关联数组 (associative array)。
值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为 可能。
JSON语法规则
对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。
数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。
值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。这些结构可以嵌套。
字符串(string)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串 (character string)。与C或者Java的字符串非常相似。
快速浏览JSON与XML表现形式
假如有一个employee对象,它有“姓、名、员工编号、头衔”等信息,先看看JSON是如何来描述它的: Js代码
1. {
2. employee : 3. {
4. firstName: \"John\5. lastName : \"Doe\6. employeeNumber : 123, 7. title : \"Accountant\" 8. } 9. }
再来看看XML是如何表示的,请看: Xml代码
1. 2. 4.
从上面描述看,JSON表示法不正是JavaScript中对象描述的一种方式么?正确,这正是JavaScript中的对象构造的原生代码。既然是原生 代码,我们把
它转换成JavaScript中的对象,这样我们操作对象就比操作字符串方便多了。 把JSON字符串转换成JavaScript对象: Js代码
1.
看完上述后我们来对比它们。
XML与JSON比对
经过一番快速浏览后如何?感觉到没有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优缺点
优点:
乍看上去,使用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方法的用法,懂的同学可以跳过。
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。它接收一个参数s,如果s不是字符串,则直接返回s。否则执行s语句。如果s语句执行结果是一个值,则直接返回此值,否则返回undefined。
另外,该方法只接受原始字符串作为参数,如果 string 参数不是原始字符串,那么该方法将不作任何改变地返回。因此请不要为 eval() 函数传递 String 对象来作为参数: Js代码
1. var str = new String(\"alert('msg')\");
2. //请不要传递String对象,否则直接返回string对象了 3. alert(eval(str)==str);//true 4.
5. //应该传递原始string字符串,这样才看作JavaScript脚本并执行 6. eval(\"alert('msg')\");//msg
最后,需要特别注意的是对象声明语法“{}”并不能返回一个值,需要用括号括起来才会返回值(括号里的脚本是表达式,有返回值,而不是无返回值的逻辑式, 因为大括号里的脚本又可能是表达式,又可能是普通的逻辑表达,所以用小括号括起来后明确的说明是值表达式): Js代码
1. var str=\"{}\";//花括号内没属性时
2. alert(eval('(' + str + ')'));//弹出:[object Object] 3. alert(eval(str));//弹出:undefined 4.
5. str=\"{name:'jzj'}\";// 花括号内有一个属性
6. alert(typeof eval('(' + str + ')'));//弹 出:object,以对象方式返回
7. alert(eval(str));//弹出:jzj
8. alert(eval('(' + str + ')').name);//弹出:jzj 9. alert(eval(str).name);//弹出:undefined 10.
11.str=\"{name:'jzj',age:30}\";//花括号内有多个属性时 12.alert(eval('(' + str + ')'));//
13.alert(eval('(' + str + ')').name);//jzj 14.alert(eval('(' + str + ')').age);//30
15.//alert(eval(str));//运行时会出错,多个属性时不用小括号运行出错 可以看到,对于对象声明语句来说,仅仅是执 行,并不能返回值 。为了返回常用的“{}”这样 的对象声明语句,必须用括号括住,以将其转换为表达式,才能返回其值 。这也是使用JSON来进行Ajax开发的基本原理之一。
现来说说本节的重点,就是在应用eval时,动态脚本所生成的变量都
局部的,但很多时候我们 可能在调用eval函数的外面使用生成的变量,eval
不可能在全局空间内执行,这就给开发带来了不少问题,这该如何作?请看继续往下看吧。
我们先来证实一下eval产生的变量是局部性的,在调用eval函数外是不能访问到此变量的。 Js代码
1. var str='全局';//定义一个全局变量 2. function test(){
3. eval('var str=\"局部\"'); 4. alert(str);//局部 5. }
6. test();
7. alert(str); //弹出:全局
另外,eval生成的函数也是局部的,它只能在生成它的函数内使用,出函数域就不能调用的到。
解决办法: Js代码
1. function gloablEval(code){
2. if(window.attachEvent && !window.opera){ 3. //ie
4. execScript(code); 5. }else{
6. //not ie window对象不能省,否则生成的变量在执行的函数外不可见
7. window.eval(code); 8. alert(str);//局部 9. //eval(code);
10. //alert(str);//局部 11. } 12.}
13.var str='全局';//定义一个全局变量 14.gloablEval('var str=\"局部\"');
15.alert(str); //弹出:局部,这里用局部的履盖了全局变量的值 现解释一下:
1、对于IE浏览器,默认已经提供了这样的函数:execScript,用于在全局空间执行代码。
2、对于Firefox浏览器,直接调用eval函数,则在调用者的空间执行;如果调用 window.eval则在全局空间执行。
JavaScript中的JSON
我们知道,可以使用eval()方法调用JavaScript的编译器把JSON文本转变成对象。因为JSON是JavaScript的一个确切的子集, 编译器可以正确地解析JSON文本,然后生成一个对象结构。 Js代码
1. var myObject = eval('(' + myJSONtext + ')');
eval函数非常快速。它可以编译执行任何JavaScript程序,因此产生了安全性问题。当使用可信任与完善的源代码时才可以使用eval函数。这样 可以更安全的使用JSON解析器。使用XMLHttpRequest的web应用,页面之间的通讯只允许同源,因此是可以信任的。但这却不是完善的。如果 服务器没有严谨的JSON编码,或者没有严格的输入验证,那么可能传送包括危险脚本的无效JSON文本。eval函数将执行恶意的脚本。
如果关心安全的话,使用JSON解析器可以防止此类事件。JSON解析器只能辨识JSON文本,拒绝所有脚本,因此它比较安全,JSON官方网站提供的一 个开源的JSON解析器和字符串转换器(http://www.json.org/json.js )。 Js代码
1. var myObject = myJSONtext.parseJSON();
而JSON的字符串转换器(stringifier)则作相反的工作,它将JavaScript数据结构转换为JSON文本。JSON是不支持循环数据结 构的,所以注意不能把循环的结构交给字符串转换器。 Js代码
1. var myJSONText = myObject.toJSONString();
Java中的JSON
Java中的JSON解释器官方提供了好几种,我们这里使用的是org.json 包,关于如何使用,请参见另一篇《JSON之org.json包测试》 ,它是基于官方包提供的测试改写而来的。
开始实战
本实例实现了客户端与服务器端通过JSON进行参数的传递与接收,而不是通过原来的XML方式进行通信。页面采用了Prototype的Ajax方式进行 异步通信,并采用了官方json.js 进行对象与JSON串的灵活互转。服务器端采用了官方提供org.json 包进行JSON串与Java对象的互转。具体的细节请看代码注释。
客户端实现:
页面代码清单代码
1. 2.
3.
5. 6.
7.
8.9.
10. 11. 88. 89.
页面展示结果:
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. 服务器端实现:
Java代码
1. package orgjson; 2.
3. import java.io.IOException; 4. import java.util.Iterator; 5. import java.util.Map;
6. import java.util.SortedMap; 7. import java.util.TreeMap; 8.
9. import javax.servlet.ServletException; 10.import javax.servlet.http.HttpServlet;
11.import javax.servlet.http.HttpServletRequest; 12.import javax.servlet.http.HttpServletResponse; 13.
14.import org.json.JSONArray; 15.import org.json.JSONException; 16.import org.json.JSONObject; 17.
18.import bean.Address; 19.
20./**
21. * 使 用 org.josn 包进行解析操作 22. * (C) 2009-9-1, jzjleo 23. */
24.public class AddressServlet extends HttpServlet { 25.
26. private static final long serialVersionUID = -1762985503942785440L; 27.
28. protected void service(HttpServletRequest req, HttpServletResponse resp)
29. throws ServletException, IOException { 30. // 创建addressbook数据结构
31. SortedMap addressBook = new TreeMap(); 32.
33. // 创建新的address实体并放置到Map中
34. Address annMichaels = new Address(\"P.O BOX 54534tle, WA\
35. \"561-832-3180\
36. addressBook.put(\"Ann Michaels\37.
38. Address bettyCarter = new Address(\"53 Mullholand Drive\", \"Miami, FL\
39. \"541-322-1723\
40. addressBook.put(\"Betty Carter\41.
42. try { 43.
44. // 准备转换通讯簿映射到JSON数组 45. // 数组用于放置多个地址条目
46. JSONArray jsonAddressBook = new JSONArray(); 47.
48. // 迭代过滤地址簿条目
49. for (Iterator iter = addressBook.entrySet().iterator(); iter.hasNext();) { 50.
51. // 找当前迭代项
52. Map.Entry entry = (Map.Entry) iter.next(); 53. String key = (String) entry.getKey();
54. Address addressValue = (Address) entry.getValue(); 55.
56. // 以键值对的形式存入条目并分配给\"name\" 57. JSONObject jsonResult = new JSONObject();
58. jsonResult.put(\"name\59.
60. // 获取和创造相应的地址结构,綁定到新Key 61. // 追加地址条目到JSON格式结果
62. String streetText = addressValue.getStreet(); 63. String cityText = addressValue.getCity(); 64. int zipText = addressValue.getZip(); 65.
66. //--注:Bean可以直接转换成JSON串,下面可以采 用 new JSONObject(addressValue)
67. //形式来自动转换成JSON串
68. JSONObject jsonAddrObj = new JSONObject(); 69.
70. //以数组的形式存放,street为存放数组的变量名即Key,如果key不存在,则新建,如果存在, 71. //则在原来数组后追加 72.
73. //jsonAddress.append(\"street\74. //上句等价于下面语句
75. jsonAddrObj.put(\"streetreetText));
76. jsonAddrObj.append(\"city\
77. jsonAddrObj.append(\"city\追加 78. //上两句等价于如下语句
79. //jsonAddrObj.put(\"city\cityText).put(\"changsha\"));
80. jsonAddrObj.append(\"zip\;
81. jsonResult.put(\"address\82.
83. // 获取和结构,建立相应的电话到新的Key 84. // 追加在电话条目到JSON格式的结果里 85. String telText = addressValue.getTel();
86. String telTwoText = addressValue.getTelTwo(); 87. JSONArray jsonTelephones = new JSONArray(); 88. jsonTelephones.put(telText); 89. jsonTelephones.put(telTwoText);
90. jsonResult.put(\"phoneNumbers\ 91.
92. // 把JSON地址条目放置到全局的JSON地址薄数组里
93. jsonAddressBook.put(jsonResult); 94. } // 结束循环地址薄
95.
96. // 赋值JSON地址薄到结果字符变量
97. JSONObject resultJsonObj = new JSONObject().put(\"addressbook\
98. jsonAddressBook); 99.
100. //格式化输出到页面上的JSON串
101. String jsonHtmlStr = resultJsonObj.toString(4).replaceAll(\" \
102. .replaceAll(\"\\n\103.
104. JSONObject jsonObj = new JSONObject().put(\"jsonHtmlStr\
105. \"jsonCode\
106. // 返回JSON串
107. resp.getOutputStream().write(jsonObj.toString().getBytes()); 108.
109. System.out.println(resultJsonObj.toString(4));
110. //System.out.println(jsonObj.toString(4)); 111. System.out.println(\"-----------------------------------------------\");
112. readJSONString(req); 113. } catch (Exception e) { 114. e.printStackTrace(); 115. } 116.
117. } 118.
119. /**
120. * 从 客户端读取JSON串 121. * @param req 122. */
123. public void readJSONString(HttpServletRequest req) { 124. System.out.println(\"接收到客户端的JSON信息如下:\");
125. JSONObject clientJSONObj; 126. try {
127. clientJSONObj = new JSONObject(req.getParameter(\"jsonStr\")); 128.
129. System.out.println(clientJSONObj.toString(4));
130. JSONArray addressbookArray = clientJSONObj.getJSONArray(\"addressbook\");
131. for (int i = 0; i < addressbookArray.length(); i++) {
132. System.out.println(\"The\" + \" \" + (i + 1) + \" addressbook msg:\");
133. JSONObject addressbookJSONObj = addressbookArray.getJSONObject(i);
134. JSONObject addressJSONObj = addressbookJSONObj.getJSONObject(\"address\");
135. System.out.println(\"address-------\"); 136. System.out.println(\" \" + addressJSONObj.getString(\"city\"));
137. System.out.println(\" \" + addressJSONObj.getString(\"street\"));
138. System.out.println(\" \" + addressJSONObj.getString(\"zip\"));
139. System.out.println(\"name----------\"); 140. System.out.println(\" \" + addressbookJSONObj.getString(\"name\"));
141. System.out.println(\"phoneNumbers--\"); 142. JSONArray phoneNumbersArr = addressbookJSONObj
143. .getJSONArray(\"phoneNumbers\"); 144. System.out.println(\" \" + phoneNumbersArr.getString(0));
145. System.out.println(\" \" + phoneNumbersArr.getString(1));
146. System.out.println(); 147. }
148. } catch (JSONException e) { 149. e.printStackTrace(); 150. } 151. } 152. }
服务器后台打印:
{\"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
因篇幅问题不能全部显示,请点此查看更多更全内容