【Android開發經驗】比Gson解析速度快10倍!——Json解析神器Jackson使用介紹,gsonjson
轉載請註明出處:http://blog.csdn.net/zhaokaiqiang1992
在前面的兩篇文章中,我們介紹了Json資料格式和系統內建Json以及Google的Gson項目,如果能學會這些東西,基本能滿足工作需求了。但是,程式員都有追求極致效率的嗜好,在滿足了基本需求之後,我們會考慮能不能再最佳化一下效率呢?當然!今天這篇文章要介紹的,就是在資料量比較大的時候,比Gson的解析效率高近10倍的Json資料解析架構— —Jackson!
下面是一個大神關於幾個常見的Json資料的解析速度的測試結果,原文請戳http://wangym.iteye.com/blog/738933
我們從可以看出,在大資料量的情況下,Jackson的速度比Gson的解析速度快了不是一點半點鐘,所以,最推薦大家使用的就是Jackson架構,來,咱們一起看看怎麼用!
我們先介紹一下常用的幾個類的功能:
JsonFactory:這個類是Jackson項目主要的Factory 方法,主要用於配置和構建解析器(比如 JsonParser)和產生器(比如 JsonGenerator),這個工廠執行個體是安全執行緒的,如果有配置的話,可以重複使用。
JsonGenerator:這個類主要是用來產生Json格式的內容的,我們可以使用JsonFactory的方法產生一個執行個體。
JsonParser:這個主要是用來讀取Json格式的內容,並且完成解析操作的,我們可以使用JsonFactory的方法產生一個執行個體。
ObjectMapper:這個類提供了Java對象和Json之間的轉化,主要通過JsonParser和JsonGenerator執行個體來完成實際的對Json資料的讀寫操作。這個類是ObjectCodec的子類,主要的實現細節都在ObjectCodec裡面。而且這個類可以重複使用,所以一般會建立這個類的一個單例模式,比如下面的代碼
package com.example.jsondemo;import org.codehaus.jackson.map.ObjectMapper;/** * * @ClassName: com.example.jsondemo.JacksonMapper * @Description:ObjectMapper的單例模式 * @author zhaokaiqiang * @date 2014-11-27 下午4:06:52 * */public class JacksonMapper {private static final ObjectMapper mapper = new ObjectMapper();private JacksonMapper() {}public static ObjectMapper getInstance() {return mapper;}}
我們在下面的代碼中,我們經常使用這個單例模式,大家不要迷惑。
1.簡單對象的Json資料產生
這裡所說的簡單對象指的是單一對象或者是對象的集合類。
下面我們先看代碼實現,然後講解一些細節。
實現的方法有兩種,先看第一種,如下:
public String getJsonString(Object object) throws Exception {return JacksonMapper.getInstance().writeValueAsString(object);} 在這種方法中,我們直接使用ObjectMapper類的writeValueAsString(),就可以把對象的json格式作為傳回值直接返回。
除此之外,我們還可以使用第二種方法,代碼如下
public String getJsonString1(Object object) throws Exception {ObjectMapper mapper = JacksonMapper.getInstance();StringWriter sw = new StringWriter();JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);mapper.writeValue(gen, object);gen.flush();gen.close();return sw.toString();} 這種方法是使用JsonGenerator和StringWriter對象,通過ObjectMapper,把轉換好的Json資料寫到了StringWriter裡面,然後toString()就可以拿到我們需要的Json資料了。
這裡再多說一句,從第二種方法的方式裡,我們可以看到操作非常像Java裡面的IO流的處理,實際上,Jackson確實是以流的方式對資料進行處理的,這也是它處理速度這麼快的原因所在。
因為第一種方法更加的簡潔,所以推薦大家使用。
下面是測試代碼和測試結果
/** * 集合對象 * * @throws Exception */public void objectsToJson() throws Exception {Person p = new Person("zhaokaiqiang", 22, new Birthday(1992, 1, 19));ArrayList<Person> persons = new ArrayList<Person>();persons.add(p);persons.add(p);persons.add(p);Log.d(TAG, getJsonString(persons));}/** * 單一對象 * * @throws Exception */public void objectToJson() throws Exception {Person p = new Person("zhaokaiqiang", 22, new Birthday(1992, 1, 19));Log.d(TAG, getJsonString(p));}
測試結果如下
{"name":"zhaokaiqiang","birthday":{"day":19,"month":1,"year":1992},"age":22}
[{"name":"zhaokaiqiang","birthday":{"day":19,"month":1,"year":1992},"age":22},{"name":"zhaokaiqiang","birthday":{"day":19,"month":1,"year":1992},"age":22},{"name":"zhaokaiqiang","birthday":{"day":19,"month":1,"year":1992},"age":22}]
2.複雜物件的Json資料的產生
這裡所說的複雜物件指的是由多種不同資料類型的資料群組成的資料,下面我們將介紹如何建立這種複雜物件的Json資料。
我們在前面介紹過,Json格式分為Object形式和Array形式,我們將分別介紹這兩種格式如何產生。
(1)Object格式
我們先直接看下面的代碼吧!
/** * 產生Object形式的json * * @throws Exception */public void createObjectJson() throws Exception {Person p = new Person("zhaokaiqiang", 22, new Birthday(1992, 1, 19));StringWriter stringWriter = new StringWriter();// 必須通過這種方式擷取JsonGenerator jsonGenerator = JacksonMapper.getInstance().getJsonFactory().createJsonGenerator(stringWriter);jsonGenerator.writeStartObject();jsonGenerator.writeStringField("name", "zhaokaiqiang");jsonGenerator.writeNumberField("age", 22);jsonGenerator.writeObjectField("person", p);jsonGenerator.writeEndObject();jsonGenerator.flush();jsonGenerator.close();Log.d(TAG, stringWriter.toString());} JsonGenerator是一個產生器,如果我們想建立複雜的Json對象,我們就可以是用這個類來完成。但是有一點必須要注意,就是擷取JsonGenerator的方式,我們必須使用ObjectMapper的getJsonFactory(),然後建立一個JsonGenerator,否則如果我們使用writeObjectField()方法的時候,會報下面的異常
java.lang.IllegalStateException: No ObjectCodec defined for the generator, can only serialize simple wrapper types (type passed com.example.jsondemo.Person)
上面的其餘代碼很簡單,只要根據不同的資料類型,調用對應的write___Field()即可。
下面是測試結果
{"name":"zhaokaiqiang","age":22,"person":{"name":"zhaokaiqiang","birthday":{"day":19,"month":1,"year":1992},"age":22}}
(2)Array格式
下面我們介紹如何產生Array格式的Json資料,其實和上面的差不多。
下面是代碼實現:
/** * 建立Array形式的json * * @throws Exception */public void createArrayJson() throws Exception {Person p = new Person("zhaokaiqiang", 22, new Birthday(1992, 1, 19));StringWriter stringWriter = new StringWriter();// 只能通過這種方式擷取JsonGenerator jsonGenerator = JacksonMapper.getInstance().getJsonFactory().createJsonGenerator(stringWriter);jsonGenerator.writeStartArray();jsonGenerator.writeString("zhaokaiqiang");jsonGenerator.writeNumber(22);jsonGenerator.writeObject(p);jsonGenerator.writeEndArray();jsonGenerator.flush();jsonGenerator.close();Log.d(TAG, stringWriter.toString());} 思路是完全一樣的,下面是測試的結果:
["zhaokaiqiang",22,{"name":"zhaokaiqiang","birthday":{"day":19,"month":1,"year":1992},"age":22}]
3.解析簡單Json對象
下面開始介紹如何解析Json資料為Java對象。
我們直接看解析代碼
public void toObject() throws Exception {ObjectMapper objectMapper = new ObjectMapper();Person p = new Person("zhaokaiqiang", 22, new Birthday(1992, 1, 19));String jsonString = getJsonString(p);Person person = objectMapper.readValue(jsonString, Person.class);Log.d(TAG, person.toString());}
在解析的時候,我們還是需要用ObjectMapper對象,調用readValue,然後傳入需要解析的json資料和轉換類型的class,就可以完成轉換了。
下面是解析簡單的集合對象的代碼
public void toObjects() throws Exception {Person p = new Person("zhaokaiqiang", 22, new Birthday(1992, 1, 19));ArrayList<Person> persons = new ArrayList<Person>();persons.add(p);persons.add(p);persons.add(p);ObjectMapper objectMapper = new ObjectMapper();String jsonString = getJsonString(persons);@SuppressWarnings("unchecked")ArrayList<Person> arrayList = objectMapper.readValue(jsonString,new ArrayList<Person>().getClass());Log.d(TAG, arrayList.toString());}
4.解析複雜的Json對象
這裡所說的複雜物件,與上面的產生的複雜Json對象是一個意思,指不是由簡單的基本類型或者是單一對象集合的Json對象。我們要進行解析測試的文本,是下面的這種形式
{"name":"zhaokaiqiang","age":22,"person":{"name":"zhaokaiqiang","birthday":{"day":19,"month":1,"year":1992},"age":22}} 這個字串中有字串類型、int類型、一個擁有對象屬性的一個自訂的對象,所以使用上面的進行類型反射的做法是完不成解析操作的,因為這個對象比較複雜,所以經過研究,我決定採用分段的方式進行解析,也就是對於這種Object類型的Json資料,分別根據key取出value,然後單獨對裡面的自訂對象進行解析,下面我們看代碼實現:
public void fromJsons() throws Exception {String jsonString = createObjectJson();ObjectMapper objectMapper = JacksonMapper.getInstance();JsonNode jsonNode = objectMapper.readTree(jsonString);JsonNode nameNode = jsonNode.get("name");JsonNode ageNode = jsonNode.get("age");JsonNode persoNode = jsonNode.get("person");String personString = persoNode.toString();Person person = objectMapper.readValue(personString, Person.class);Log.d(TAG, "person=" + person.toString());Log.d(TAG, "age=" + ageNode.asInt());Log.d(TAG, "name=" + nameNode.asText());}
我們在這裡使用了ObjectMapper的readTree方法,返回一個JsonNode對象,我們在這裡採用的是樹模型的解析方式,類似XML中的DOM。
如果我們想擷取基礎資料型別 (Elementary Data Type)的資料,那麼我們使用JsonNode.get()根據key擷取到包含了value的JsonNode對象之後,根據類型,使用as__()即可將需要擷取的資料提取出來。JsonNode代表的是一個節點,是Json的樹模型的一個基礎類。
但是如果想擷取類型是非基礎類型的資料的時候,JsonNode並沒有提供對應的方法,所以我們只能使用JsonNode的toString方法,擷取到Json格式的資料,然後根據我們前面介紹的解析簡單對象的方法再次解析即可。但是有一個注意點,就是我們擷取key為"person"的資料的時候,不能夠使用JsonNode.asText()來擷取,因為這並不是一個文本,而是一個Object形式的另外一個Json對象,這樣做會擷取不到的,所以我用的toString()擷取。
當然,這隻是我自己探索出來的一個方法,還有其他的方案,比如直接建立這種類型的JavaBean,但是比較麻煩,上面的這種方法更加直觀。
5.總結
到現在為止,Android平台下主流的三種Json解析方式已經介紹完畢,我們來總結一下:
(1)Android內建的Json類庫,效率低,使用繁瑣,不支援反射解析,不推薦使用
(2)google的Gson解析庫,這個是我之前一直使用的類庫,使用方便,資料比較多,效率對於小量的資料解析來說沒有問題,也是很推薦大家使用
(3)jackson是效率最高的解析方案,如果資料量巨大,這個方案絕對是首選。如果資料量比較小,和Gson相比差別不是很大,自己哪個順手用那個。
除了上面介紹的Jackson,Jackson還有其他更強大的用法,這裡就不一一介紹了,用到再說,下面是幾個參考網址:
(1)Jackson線上文檔:http://tool.oschina.net/apidocs/apidoc?api=jackson-1.9.9
(2)Jackson介紹的官網翻譯:http://www.cnblogs.com/lee0oo0/articles/2652528.html
(3)Jackson源碼查看:http://www.boyunjian.com/javasrc/org.codehaus.jackson/jackson-core-lgpl/1.9.10/_/
(4)線上Json驗證:http://www.bejson.com/
文章中的測試Demo:https://github.com/ZhaoKaiQiang/JsonDemo