今天很高兴和大家一起学习Android的JSON数据解析,可能对于学习安卓的朋友都知道JSON在数据解析方面已经很普遍了.所以也是我们必定要了解的知识 ,下面让我们了解一下JSON的发展历程.
XML——这种用于表示客户端与服务器间数据交换有效负载的格式,几乎已经成了Web services的同义词。然而,由于Ajax和REST技术的出现影响了应用程序架构,这迫使人们开始寻求`XML的替代品,如:JavaScript Object Notation(JSON)。
JSON 作为一种更轻、更友好的 Web services客户端的格式(多采用浏览器的形式或访问 REST风格 Web服务的Ajax应用程序的形式)引起了 Web 服务供应商的注意。
本文将阐述JSON在Web services设计中备受推崇的原因,以及它作为XML替代方案的主要优势和局限性。文中还会深入探讨:随着相应的Web 服务客户端选择使用JSON,如何才能便捷地在Java Web services中生成JSON输出。
XML的十字路口: 浏览器和 Ajax
XML设计原理已经发布了将近十年。时至今日,这种标记语言已经在广阔的软件应用领域中占据了主导地位。从Java、.NET等主流平台中的配置和部署描述符到应用集成场景中更复杂的应用,XML与生俱来的语言无关性使之在软件架构师心目中占据着独特的地位。但即便最著名的XML权威也不得不承认:在某些环境中,XML的使用已经超出了它自身能力的极限。
围绕Ajax原理构建的那些Web应用程序最能说明XML的生存能力,从这一点来看,一种新的有效负载格式的发展壮大也得益于XML。这种新的有效负载格式就是JavaScript Object Notation (JSON)。在探索这种新的标记语言的复杂性之前,首先来分析一下在这种独特的设计形式中,XML具有哪些局限性。
Ajax建立了一个用于从远程Web services发送和接收数据的独立信道,从而允许Web程序执行信道外(out-of-band)客户端/服务器调用。通俗地说,Ajax程序中的更新和导航序列在典型的客户端/服务器环境之外完成,在后台(即信道外)接受到信息后,必须进行一次完整的屏幕刷新。更多背景信息,请参阅David Teare的 Ajax简介(Dev2Dev)。
这些应用程序更新通常是通过REST风格(RESTful)Web services获得的,一旦被用户的浏览器接收到,就需要整合到HTML页面的总体布局之中,这正是XML发挥强大力量的场合。尽管近年来,脚本语言支持和插件支持已使大多数主流浏览器的功能得到了强化,但许多编程任务依然难于开展,其中之一就是操纵或处理文本,这通常是使用DOM实现的。
采用DOM的复杂性源于其基于函数的根,这使得对数据树的简单修改或访问都需要进行无数次方法调用。此外,众所周知,DOM在各种浏览器中的实现细节不尽相同,这一过程将带来极为复杂的编程模式,其跨浏览器兼容性出现问题的可能性极大。接下来的问题显而易见,那就是:如何使一种标记语言轻松集成到HTML页面中以满足Ajax的要求?
问题的答案就是:利用所有主流浏览器中的一种通用组件——JavaScript引擎。XML需要使用DOM之类的机制来访问数据并将数据整合到布局之中,采用这种方法,我们不再使用像XML这样的格式来交付Ajax更新,而是采用一种更为简单直观的方式,采用JavaScript引擎自然匹配的格式——也就是JSON。
既然已经明确了JSON与XML和Ajax之间的关系,下面将进一步探讨JSON背后的技术细节。
JSON剖析:优点和不足
对于JSON,首先要明白JSON和XML一样也是一种简单文本格式。相对于XML,它更加易读、更便于肉眼检查。在语法的层面上,JSON与其他格式的区别是在于分隔数据的字符,JSON中的分隔符限于单引号、小括号、中括号、大括号、冒号和逗号。下图是一个JSON有效负载:
- {"addressbook": {"name": "Mary Lebow",
- "address": {
- "street": "5 Main Street"
- "city": "San Diego, CA",
- "zip": 91912,
- },
- "phoneNumbers": [
- "619 332-3452",
- "664 223-4667"
- ]
- }
- }
上面的格式没看清楚不要紧,我们慢慢来分析.
下面我们来看看JSON的数据结构:
1.JSON数据结构
在JSON中有两种数据结构:对象和数组。
1.1对象
在JSON中,一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号),冒号后是该名称的值,多个“名称:值”之间使用 “,”(逗号)分隔开来。名称需要使用双引号括起来,值如果是字符串则必须用双引号括起来,如果是数值型则不需要。其结构示意图如图1所示。
例如:
- {
- "name":"BruceCheng",
- "age":22,
- "address":"安徽省合肥市"
- }
1.2数组
在JSON中,数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用 “,”(逗号)分隔开来。其结构示意图如图2所示。
例如:
- ["Android","IOS","Syphone","BlackBerry"]
那么在JSON数据中值的类型有哪些呢?
竟然JSON的数据类型是多种多样的那么我们就可以利用不同类型的数据组合成复杂的数据类型:
- {"addressbook": {"name": "Mary Lebow",
- "address": {
- "street": "5 Main Street"
- "city": "San Diego, CA",
- "zip": 91912,
- },
- "phoneNumbers": [
- "619 332-3452",
- "664 223-4667"
- ]
- }
- }
我们来分析分析:
从上面整体来看是一个Object类型,该Object类型中有一个成员为:"addressbook"
"addressbook"的数据结构又是一个Object类型,
该Object类型又包含三个成员,分别是"name","address","phoneNumbers"
这三个成员数据结构的类型分别是:String,Object,Array,
"name"的值为String类型
"address"的数据结构为Object类型,它又包含三个子成员,分别是:"street","city","zip",这三个成员的数据结构类型的类型均为String类型,
"phoneNumbers"成员的数据结构为数组类型,包含两个成员为String类型.
JSON解析分为两种情况,一种是在服务器端解析,另一种是在客户端解析
第一种:在服务器段解析:
通常,客户端在请求服务器数据时,服务器可以使用XML文档、JSON数据或HTML的形式将数据发送给客户端。
那么如何在服务器端生成JSON数据呢?首先需要完成以下两个准备工作。
(1)我们需要使用Eclipse创建了一个Web Project,这里我将该工程命名为了“AndroidToJSON解析”(方便查看,开发时不要用中文),用来模拟服务器端的Web服务。
(2)我们还需要在该工程中导入JSON的API数据包json-lib-2.2.2-jdk15.jar。
下载地址:http://download.youkuaiyun.com/detail/u013059555/6673615
在JSON的API中,提供了JSONObject类,通过调用JSONObject类的put(Object key, Object value)方法,可以将一个Object对象以键值对的形式存入JSONObject对象。通过调用JSONObject类的toString()方法,则可以将JSONObject对象转化为JSON数据。
如下的代码创建了一个JsonTools类,并实现了静态方法createJsonString(),用来生成JSON数据。
- public class JsonTools {
- /**
- *
- * @param key json数据的键值
- * @param value 生成的json数据的value值
- * @return json对象的值
- * @author brucecheng夏夏
- */
- public static String createJsonString(String key,Object value){
- //创建
- JSONObject json=new JSONObject();
- //此处要try----catch一下
- try {
- json.put(key, value);
- } catch (JSONException e) {
- e.printStackTrace();
- }
- return json.toString();
- }
通过使用该方法,我们可以很方便的将各种数据传递进来,并将其转化成JSON数据。比如,我们可以在JsonService类中,实现一个简单的获取Person对象列表的方法,具体如下:
编写一个JavaBeans:
Person类:id,name,age三个属性
- public class JsonService {
- public List<Person> getListPerson(){
- List<Person> list=new ArrayList<Person>();
- Person p1=new Person(1001, "Bruce", 20);
- Person p2=new Person(1002, "Jack", 21);
- Person p3=new Person(1003, "David", 22);
- list.add(p1);
- list.add(p2);
- list.add(p3);
- return list;
- }
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html;charset=utf-8");
- request.setCharacterEncoding("utf-8");
- response.setCharacterEncoding("utf-8");
- PrintWriter out = response.getWriter();
- List<Person> listPerson = jsonService.getListPerson();
- String str = null;
- String action_flag = request.getParameter("action_flag"); //获取URL参数
- if(action_flag.equals("persons")) {
- str = JsonTools.createJsonString("persons", listPerson);
- }
- out.println(str);
- out.flush();
- out.close();
- }
可以看到,在doPost()方法中,我们通过调用getListPerson()方法获得了Person对象列表listPerson,并将其传入JsonTools.createJsonString()方法中,从而获得了一串JSON数据。
将该工程发布到Tomcat上,使用浏览器访问该Web工程,可以看到如图4所示的界面,Person对象列表被成功的转化成了JSON数据。
以上就成功的将数据部署到服务器中了,
接下来我们新建一个安卓的工程来获取相应的解析数据:
当点击Button时将解析出来的数据绑定到textView上去
布局文件代码如下:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <Button
- android:id="@+id/btn_json"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/json"
- android:textColor="#0000ff"
- android:textSize="20sp" />
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@id/btn_json" >
- <TextView
- android:id="@+id/tv_show"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/hello_world"
- android:textColor="#0000ff"
- android:textSize="20sp" />
- </ScrollView>
- </RelativeLayout>
要完成解析我们需要编写以下几个类:
第一个:
Person类的java beans和之前的一样不再重写
第二个:
JsonTools类其功能是根据key和json数据来解析获得数据
- package com.brucecheng.json;
- import java.util.ArrayList;
- import java.util.List;
- import org.json.JSONArray;
- import org.json.JSONObject;
- public class JsonTools {
- //自定义一个方法解析
- public static List<Person> getPerson(String key,String jsonString){
- List<Person> persons=new ArrayList<Person>();
- try {
- //获取Json对象
- JSONObject jsonObj=new JSONObject(jsonString);
- //由Json对象获取JsonArray
- JSONArray jsonArr=jsonObj.getJSONArray(key);
- //遍历数组获得每一个jsonObject对象
- for (int i = 0; i < jsonArr.length(); i++) {
- JSONObject personObj=jsonArr.getJSONObject(i);
- int id=personObj.getInt("id");
- String name=personObj.getString("name");
- int age=personObj.getInt("age");
- Person p=new Person(id,name,age);
- persons.add(p);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return persons;
- }
- }
第三个:
HttpUtils类,本类的作用是根据路径打开连接获取json数据
- package com.brucecheng.json;
- import java.io.ByteArrayOutputStream;
- import java.io.InputStream;
- import java.net.HttpURLConnection;
- import java.net.URL;
- public class HttpUtils {
- //定义一个静态的方法获取JSON内容
- //这里的path是web服务的网址
- public static String getJsonContent(String path){
- try {
- //根据路径创建URL地址
- URL url=new URL(path);
- //通过url地址打开连接
- HttpURLConnection conn=(HttpURLConnection) url.openConnection();
- //设置超时时间
- conn.setConnectTimeout(10000);
- //设置请求方式
- conn.setRequestMethod("GET");
- //设置属性
- conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)");
- //设置该连接是否可输入
- //conn.setDoInput(true);
- int code=conn.getResponseCode();
- System.out.println(code+"****");
- if(code==200){
- return changeInputString(conn.getInputStream());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return "";
- }
- //自定义方法根据io流得到字符串
- public static String changeInputString(InputStream is){
- String jsonString="";
- ByteArrayOutputStream baos=new ByteArrayOutputStream();
- byte[] data=new byte[1024];
- int len=0;
- try {
- while((len=is.read(data))!=-1){
- baos.write(data,0,len);
- }
- jsonString=new String(baos.toByteArray());
- } catch (Exception e) {
- e.printStackTrace();
- }
- return jsonString;
- }
- }
运行结果如下:
json规范rfc4627: http://www.ietf.org/rfc/rfc4627.txt
json介绍: http://www.json.org/json-zh.html
json入门参考: http://www.cnblogs.com/Truly/archive/2006/12/31/608896.html
android2.3提供的json解析类
android的json解析部分都在包org.json下,主要有以下几个类:
JSONObject:可以看作是一个json对象
JSONStringer:json文本构建类
JSONArray:可以看作是json的数组
JSONTokener:json解析类
JSONException:json中用到的异常
JSONObject, JSONArray来构建json文本
- // 假设现在要创建这样一个json文本
- // {
- // "phone" : ["12345678", "87654321"], // 数组
- // "name" : "yuanzhifei89", // 字符串
- // "age" : 100, // 数值
- // "address" : { "country" : "china", "province" : "jiangsu" }, // 对象
- // "married" : false // 布尔值
- // }
- try {
- // 首先最外层是{},是创建一个对象
- JSONObject person = new JSONObject();
- // 第一个键phone的值是数组,所以需要创建数组对象
- JSONArray phone = new JSONArray();
- phone.put("12345678").put("87654321");
- person.put("phone", phone);
- person.put("name", "yuanzhifei89");
- person.put("age", 100);
- // 键address的值是对象,所以又要创建一个对象
- JSONObject address = new JSONObject();
- address.put("country", "china");
- address.put("province", "jiangsu");
- person.put("address", address);
- person.put("married", false);
- } catch (JSONException ex) {
- // 键为null或使用json不支持的数字格式(NaN, infinities)
- throw new RuntimeException(ex);
- }
getType和optType api的使用
getType可以将要获取的键的值转换为指定的类型,如果无法转换或没有值则抛出JSONException
optType也是将要获取的键的值转换为指定的类型,无法转换或没有值时返回用户提供或这默认提供的值
- try {
- // 所有使用的对象都是用上面创建的对象
- // 将第一个电话号码转换为数值和将名字转换为数值
- phone.getLong(0);
- person.getLong("name"); // 会抛异常,因为名字无法转换为long
- phone.optLong(0); // 代码内置的默认值
- phone.optLong(0, 1000); // 用户提供的默认值
- person.optLong("name");
- person.optLong("name", 1000); // 不像上面那样抛异常,而是返回1000
- } catch (JSONException ex) {
- // 异常处理代码
- }
除了上面的两个类,还可以使用JSONStringer来构建json文本
- try {
- JSONStringer jsonText = new JSONStringer();
- // 首先是{,对象开始。object和endObject必须配对使用
- jsonText.object();
- jsonText.key("phone");
- // 键phone的值是数组。array和endArray必须配对使用
- jsonText.array();
- jsonText.value("12345678").value("87654321");
- jsonText.endArray();
- jsonText.key("name");
- jsonText.value("yuanzhifei89");
- jsonText.key("age");
- jsonText.value(100);
- jsonText.key("address");
- // 键address的值是对象
- jsonText.object();
- jsonText.key("country");
- jsonText.value("china");
- jsonText.key("province");
- jsonText.value("jiangsu");
- jsonText.endObject();
- jsonText.key("married");
- jsonText.value(false);
- // },对象结束
- jsonText.endObject();
- } catch (JSONException ex) {
- throw new RuntimeException(ex);
- }
json文本解析类JSONTokener
按照RFC4627规范将json文本解析为相应的对象。
对于将json文本解析为对象,只需要用到该类的两个api:
构造函数
public Object nextValue();
- // {
- // "phone" : ["12345678", "87654321"], // 数组
- // "name" : "yuanzhifei89", // 字符串
- // "age" : 100, // 数值
- // "address" : { "country" : "china", "province" : "jiangsu" }, // 对象
- // "married" : false // 布尔值
- // }
- private static final String JSON =
- "{" +
- " \"phone\" : [\"12345678\", \"87654321\"]," +
- " \"name\" : \"yuanzhifei89\"," +
- " \"age\" : 100," +
- " \"address\" : { \"country\" : \"china\", \"province\" : \"jiangsu\" }," +
- " \"married\" : false," +
- "}";
- try {
- JSONTokener jsonParser = new JSONTokener(JSON);
- // 此时还未读取任何json文本,直接读取就是一个JSONObject对象。
- // 如果此时的读取位置在"name" : 了,那么nextValue就是"yuanzhifei89"(String)
- JSONObject person = (JSONObject) jsonParser.nextValue();
- // 接下来的就是JSON对象的操作了
- person.getJSONArray("phone");
- person.getString("name");
- person.getInt("age");
- person.getJSONObject("address");
- person.getBoolean("married");
- } catch (JSONException ex) {
- // 异常处理代码
- }
其它的api基本就是用来查看json文本中的文本的
- try {
- JSONTokener jsonParser = new JSONTokener(JSON);
- // 继续向下读8个json文本中的字符。此时刚开始,即在{处
- jsonParser.next(8); //{ "phone。tab算一个字符
- // 继续向下读1个json文本中的字符
- jsonParser.next(); //"
- // 继续向下读取一个json文本中的字符。该字符不是空白、同时也不是注视中的字符
- jsonParser.nextClean(); //:
- // 返回当前的读取位置到第一次遇到'a'之间的字符串(不包括a)。
- jsonParser.nextString('a'); // ["12345678", "87654321"], "n(前面有两个空格)
- // 返回当前读取位置到第一次遇到字符串中(如"0089")任意字符之间的字符串,同时该字符是trimmed的。(此处就是第一次遇到了89)
- jsonParser.nextTo("0089"); //me" : "yuanzhifei
- // 读取位置撤销一个
- jsonParser.back();
- jsonParser.next(); //i
- // 读取位置前进到指定字符串处(包括字符串)
- jsonParser.skipPast("address");
- jsonParser.next(8); //" : { "c
- // 读取位置前进到执行字符处(不包括字符)
- jsonParser.skipTo('m');
- jsonParser.next(8); //married"
- } catch (JSONException ex) {
- // 异常处理代码
- }
json中的null和java中的null注意:
- // json对象的成员为null可能有两种情况:
- // 1: 不出现该成员的名称(对应java中的null)
- // 2: 成员值为null。(对应java中的JSONObject.NULL)
- // 完整的Json
- // {
- // "phone" : ["12345678", "87654321"], // 数组
- // "name" : "yuanzhifei89", // 字符串
- // "age" : 100, // 数值
- // "address" : { "country" : "china", "province" : "jiangsu" }, // 对象
- // "married" : false // 布尔值
- // }
- // 第一种情况:不出现某成员的名称(address)
- String jsonText = "{" +
- " \"phone\" : [\"12345678\", \"87654321\"]," +
- " \"name\" : \"yuanzhifei89\"," +
- " \"age\" : 100," +
- " \"married\" : false," +
- "}";
- try {
- JSONTokener t = new JSONTokener(jsonText);
- JSONObject obj = (JSONObject) t.nextValue();
- if (obj.optJSONObject("address") == null || obj.isNull("address")) {
- }
- } catch (JSONException ex) {
- ex.printStackTrace();
- }
- // 第二种情况:成员值为null(address为null)
- String jsonText = "{" +
- " \"phone\" : [\"12345678\", \"87654321\"]," +
- " \"name\" : \"yuanzhifei89\"," +
- " \"age\" : 100," +
- " \"address\" : null," +
- " \"married\" : false," +
- "}";
- try {
- JSONTokener t = new JSONTokener(jsonText);
- JSONObject obj = (JSONObject) t.nextValue();
- // 应该这样判断json是否为null
- if (obj.get("address") == JSONObject.NULL || obj.isNull("address")) {
- }
- } catch (JSONException ex) {
- ex.printStackTrace();
- }
json中的对象不存在和java中的对应关系
- json中的null对应java中的JSONObject.NULL,所以jsonObj.put("key", JSONObject.NULL) 相当于{"key" : null}
- json中的不出现某成员对应Java中的null,所以jsonObj.put("key", null)相当于删除该成员,即:{}