Java中json的使用-7種開源庫介紹與比較
有效選擇七個關於Java的JSON開源類庫
April 4, 2014 By Constantin Marian Alin
翻譯:無若
(英語原文:http://www.developer.com/lang/jscript/top-7-open-source-json-binding-providers-available-today.html)
簡介
JSON是JavaScript Object Notation的縮寫,是一種輕量級的資料交換形式,是一種XML的替代方案,而且比XML更小,更快而且更易於解析。因為JSON描述物件的時候使用的是JavaScript語法,它是語言和平臺獨立的,並且這些年許多
(一)類庫介紹及其使用
(1)使用Jackson類庫
第一個介紹的是Jackson類庫,Jackson庫是一個“旨在為開發者提供更快,更正確,更輕量級,更符合人性思維” 的類庫。Jackson為處理JSON格式提供了三種模型的處理方法。
1、流式API或者增量解析/產生( incremental parsing/generation):讀寫JSON內容被作為離散的事件。
2、樹模型:提供一個可變記憶體樹表示JSON文件。
3、資料繫結(Data binding):實現JSON與POJO(簡單的Java物件(Plain Old Java Object))的轉換
我們感興趣的是Java物件與JSON的轉換,因此,我們將集中於第三種處理方法。首先我們需要下載Jackson。Jackson的核心功能使用三個類庫,分別是jackson-core-2.3.1, jackson-databind-2.3.1和jackson-annotations-2.3.1; 三個類庫的下載都來自於Maven倉庫,給出地址:
現在,讓我們來工作吧,為了從Java物件中獲得一個一個複雜的
public class JsonThirdObject {
private int age = 81;
private String name = "Michael Caine";
private List<String> messages;
public JsonThirdObject() {
this.messages = new ArrayList<String>() {
{
add("You wouldn't hit a man with no trousers..");
add("At this point, I'd set you up with a..");
add("You know, your bobby dangler, giggle stick,..");
}
};
}
// Getter and setter
}
public class JsonSecondObject {
private int age = 83;
private String name = "Clint Eastwood";
private JsonThirdObject jsnTO = new JsonThirdObject();
private List<String> messages;
public JsonSecondObject() {
this.messages = new ArrayList<String>() {
{
add("This is the AK-47 assault..");
add("Are you feeling lucky..");
add("When a naked man's chasing a..");
}
};
}
// Getter and setter
}
public class JsonFirstObject {
private int age = 76;
private String name = "Morgan Freeman";
private JsonSecondObject jsnSO = new JsonSecondObject();
private List<String> messages;
public JsonFirstObject() {
this.messages = new ArrayList<String>() {
{
add("I once heard a wise man say..");
add("Well, what is it today? More..");
add("Bruce... I'm God. Circumstances have..");
}
};
}
// Getter and setter
}
public class Json {
private int age = 52;
private String name = "Jim Carrey";
private JsonFirstObject jsnFO = new JsonFirstObject();
private List<String> messages;
public Json() {
this.messages = new ArrayList<String>() {
{
add("Hey, maybe I will give you..");
add("Excuse me, I'd like to..");
add("Brain freeze. Alrighty Then I just..");
}
};
}
// Getter and setter
}
上面的Java物件轉換成JSON格式是下面這樣的。
{ "age":52, "name":"Jim Carrey", "jsnFO":{ "age":76, "name":"Morgan Freeman", "jsnSO":{ "age":83, "name":"Clint Eastwood", "jsnTO":{ "age":81, "name":"Michael Caine", "messages":[ "You wouldn't hit a man..", "At this point, I'd set you..", "You know, your bobby dangler.." ] }, "messages":[ "This is the AK-47 assault..", "Are you feeling lucky..", "When a naked man's chasing a.." ] }, "messages":[ "I once heard a wise man..", "Well, what is it today? More..", "Bruce... I'm God. Circumstances have.." ] }, "messages":[ "Hey, maybe I will give you a call..", "Excuse me, I'd like to ask you a few..", "Brain freeze. Alrighty Then I just heard.." ] }
現在,讓我們來看看怎麼樣把Java物件轉換成JSON並且寫入檔案。Jackson使用一個ObjectMapper功能,我們第一步要做的是:
Json jsonObj = new Json();
ObjectMapper mapper = new ObjectMapper();
然後,我們將會使用這個ObjectMapper直接寫入值到檔案。
System.out.println("Convert Java object to JSON format and save to file");
try {
mapper.writeValue(new File("c:\\jackson.json"), jsonObj);
} catch (JsonGenerationException e) {
} catch (JsonMappingException e) {
} catch (IOException e) {
}
現在,我們有了一個JSON檔案,但是,怎麼樣轉回Java物件呢?我們可以這樣做:
System.out.println("Read JSON from file, convert JSON back to object");
try {
jsonObj = mapper.readValue(new File("c:\\jackson.json"), Json.class);
} catch (JsonGenerationException e) {
} catch (JsonMappingException e) {
} catch (IOException e) {
}
從上面的例子我們知道了JSON和Java物件的相互轉換,在try-catch中總共也就兩行,看起來不錯是吧,但是它快麼?我們將會在後面的文章中揭曉。
(2)使用 Google-Gson類庫
第二種就是 Google-Gson,我們立即開始討論 Gson,你或許更喜歡他的全名Google-Gson。Gson能實現Java物件和JSON之間的相互轉換。甚至都不需要註釋。Gson的特點:
1)提供簡單的toJson()方法和fromJson()去實現相互轉換。
2)可以從JSON中轉換出之前存在的不可改變的物件。
3)擴充套件提供了Java泛型。
4)支援任意複雜的物件。
Json jsonObj = new Json();
Gson gson = new Gson();
System.out.println("Convert Java object to JSON format and save to file");
try (FileWriter writer = new FileWriter("c:\\gson.json")) {
writer.write(gson.toJson(jsonObj));
} catch (IOException e) {
}
JSON轉換成Java物件:
System.out.println("Read JSON from file, convert JSON string back to object");
try (BufferedReader reader = new BufferedReader(new FileReader("c:\\gson.json"))) {
jsonObj = gson.fromJson(reader, Json.class);
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
上面就是我們所有需要做的,接下來我們可以對 jsonObj 作進一步處理。當呼叫JSON操作的時候,因為Gson的例項化物件沒有維持任何狀態,我們可以重複使用一個物件為多個JSON序列化和反序列化操作。
(3)使用JSON-lib類庫
JSON-lib類庫是基於Douglas Crockford的工作成果,能轉換bean,map,集合(collection),java陣列和XML轉換成JSON並能反向轉換成beans和動態bean(DynaBean)。JSON-lib類庫的下載地址:http://sourceforge.net/projects/json-lib/files/ 下面這些是依賴檔案
(譯者注:Douglas Crockford是Web開發領域最知名的技術權威之一,ECMA JavaScript2.0標準化委員會委員。被JavaScript之父Brendan Eich稱為JavaScript的大宗師(Yoda)。曾任Yahoo!資深JavaScript架構師,現任PayPal高階JavaScript架構師。他是JSON、JSLint、JSMin和ADSafe的創造者,也是名著《JavaScript: The Good Parts》(中文版《JavaScript語言精粹》)的作者。撰寫了許多廣為流傳、影響深遠的技術文章,包括“JavaScript:世界上最被誤解的語言”。Douglas Crockford曾在著名的Lucasfilm電影公司任技術總監;在Paramount(派拉蒙)公司任新媒體高階總監;communities社群創始人兼CEO;State軟體公司CTO。2012.05.14,Paypal宣佈Douglas Crockford加入Paypal。)
同樣,讓我們來把Java物件轉成JSON。
Json jsonObj = new Json();
JSONObject json;
System.out.println("Convert Java object to JSON format and save to file");
try (FileWriter writer = new FileWriter("c:\\json-lib.json")) {
json = JSONObject.fromObject(jsonObj);
json.write(writer);
} catch (IOException e) {
}
JSON轉Java物件
System.out.println("Read JSON from file, convert JSON string back to object");
try (BufferedReader reader = new BufferedReader(new FileReader("c:\\json-lib.json"))) {
jsonObj = (Json) JSONObject.toBean(JSONObject.fromObject(reader), Json.class);
} catch (IOException ex) {
}
這裡有個問題,這些依賴關係會影響到效能嗎?文章在下面揭曉。
(4)使用Flexjson類庫
Flexjson是一個輕量級的庫,能序列化和反序列化Java物件和JSON,允許深層和淺層物件的拷貝。深度拷貝意味著一個被Flexjson序列化的物件,它能讓物件做到類似於延遲載入(lazy-loading)的技術,能讓我們在對物件有需要時才提取。當我們想把整個物件寫入到檔案時,這不是一個好的情況,但是它知道需要才去做時,這是很好的。
Json jsonObj = new Json();
JSONSerializer serializer = new JSONSerializer();
System.out.println("Convert Java object to JSON format and save to file");
try (FileWriter writer = new FileWriter("c:\\flexjson.json")) {
serializer.deepSerialize(jsonObj, writer);
} catch (IOException e) {
}
JSON轉Java物件
System.out.println("Read JSON from file, convert JSON string back to object");
try (BufferedReader reader = new BufferedReader(new FileReader("c:\\flexjson.json"))){
jsonObj = new JSONDeserializer<Json>().deserialize(reader);
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
簡單有效是吧!
(5)使用Json-io類庫
json-io有兩個主要的類,一個讀和一個寫,排除了使用ObjectInputStream和 ObjectOutputStream兩個類去讀寫。Json-io能序列化任意的Java物件圖(graph)轉變成JSON,並且能記憶完整的語義圖(graph semantics)和物件型別。下載地址: Maven Central Repository
它不需要其他依賴。
例子:Java物件轉JSON
Json jsonObj = new Json();
System.out.println("Convert Java object to JSON format and save to file");
try (JsonWriter writer = new JsonWriter(new FileOutputStream("c:\\json-io.json"))){
writer.write(jsonObj);
} catch (IOException e) {
}
JSON轉Java物件
System.out.println("Read JSON from file, convert JSON string back to object");
try (JsonReader reader = new JsonReader(new FileInputStream(new File("c:\\json-io.json")))) {
jsonObj = (Json) reader.readObject();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
它的文件上說,Json-io比JDK的ObjectInputStream 和ObjectOutputStream的序列化操作要快,我們將會在後面的文章中說明。
(6)使用Genson類庫
Genson是一個可擴充套件的,可伸縮的,易於使用的開源庫。除此之外,Genson完整支援了泛型,支援JSON在JAX-RS的實現,支援JAXB的註釋(annotation)和型別(types),並且允許序列化和反序列化擁有複雜關鍵字的map。
例子:Java物件轉JSON
Json jsonObj = new Json();
Genson genson = new Genson();
System.out.println("Convert Java object to JSON format and save to file");
try (FileWriter writer = new FileWriter("c:\\genson.json")) {
writer.write(genson.serialize(jsonObj));
} catch (IOException | TransformationException e) {
}
JSON轉Java物件
System.out.println("Read JSON from file, convert JSON string back to object");
try (BufferedReader reader = new BufferedReader(new FileReader("c:\\genson.json"))) {
jsonObj = genson.deserialize(reader, Json.class);
} catch (FileNotFoundException e) {
} catch (IOException | TransformationException e) {
}
(7)使用JSONiJ類庫
它不需要任何依賴。
例子:Java物件轉JSON
Json jsonObj = new Json();
System.out.println("Convert Java object to JSON format and save to file");
try (FileWriter writer = new FileWriter("c:\\jsonij.json")) {
writer.write(JSONMarshaler.marshalObject(jsonObj).toJSON());
} catch (IOException | JSONMarshalerException e) {
}
JSON轉Java 物件
System.out.println("Read JSON from file, convert JSON string back to object");
try (BufferedReader reader = new BufferedReader(new FileReader("c:\\jsonij.json"))) {
JSON json = JSON.parse(reader);
// Now we need to parse the JSONObject object and put values back
// to our Json object
for (Field field : jsonObj.getClass().getDeclaredFields()) {
try {
field.setAccessible(true);
field.set(field.getName(), json.get(field.getName()));
} catch (IllegalArgumentException | IllegalAccessException e) {
}
}
} catch (FileNotFoundException e) {
} catch (IOException | ParserException e) {
}
看起來JSONiJ需要的程式碼多些,效能怎麼樣,我們看下面。
(二)基準測試
現在我們要來看看效能了,測試硬體配置:Intel Core i5 laptop with 2.50GHz 單通道DDR3 RAM 4G,軟體配置:Windows 7 Ultimate 64-bit SP1
基準測試執行一樣的虛擬機器(JVM),在測試之前,每一個類庫都有一個熱身,去限制記憶體使用的造成的影響,用一個顯式呼叫垃圾收集器。下面的圖表代表的是序列化和反序列化JSON資料以毫秒級使用50次迭代和10次熱身(warm-up)迭代的平均的時間。
(譯者注:紅色為序列化(Java物件轉JSON),藍色為反序列化(JSON轉Java物件))
上面的圖表顯示,Flexjson序列化小資料時是最快的,而JSON-lib是最慢的。反序列化的時候,Gson最快,JSON-lib還是最慢的。
下面的圖表代表的是我們的資料在287kb時,序列化和反序列化所花費的平均時間。
這張圖顯示,我們對少量的資料操作時,最快的是Gson ,之後的是 Genson和Flexjson。
當變成大資料時,結果變得很不一樣。在下面的圖表中,使用的是108Mb的資料,在序列化的時候,Jackson變成了最快的,Flexjson變成第二快。在反序列化的時候,JSON-lib變成了最快的,之前在處理小資料時,它是最慢的,第二快的是Jackson。
下面的圖表,顯示的是處理更大一點的資料時,我們應該使用Jackson和JSON-lib。
另外一個重要的測試是關於.jar包的大小。這對於移動端的開發很重要,我們從下圖中看到,json-io最小,之後依次是Flexjson和JSONiJ:
(三)結論
在這篇文章中,我們知道了七種方式來實現Java物件和JSON之間的互相轉換。以及哪一個類庫更快,哪一個更慢,在什麼情況下使用等。作為結論,如果你在你的應用中是想使用小一點的資料量,你應該使用Flexjson或者Gson,如果你需要大的資料量你應該考慮Jackson 和JSON-lib。