簡(jiǎn)介
JSON是JavaScript Object Notation的縮寫,是一種輕量級(jí)的數(shù)據(jù)交換形式,是一種XML的替代方案,而且比XML更小,更快而且更易于解析。因?yàn)镴SON描述對(duì)象的時(shí)候使用的是JavaScript語法,它是語言和平臺(tái)獨(dú)立的,并且這些年許多JSON的解析器和類庫被開發(fā)出來。在這篇文章中,我們將會(huì)展示7種Java JSON類庫。基本上,我們將會(huì)試著把Java對(duì)象轉(zhuǎn)換JSON格式并且存儲(chǔ)到文件,并且反向操作,讀JSON文件轉(zhuǎn)換成一個(gè)對(duì)象。為了讓文章更有意義,我們將會(huì)測(cè)量每一種JSON類庫在不同情況下的處理速度。
(一)類庫介紹及其使用
(1)使用Jackson類庫
第一個(gè)介紹的是Jackson類庫,Jackson庫是一個(gè)“旨在為開發(fā)者提供更快,更正確,更輕量級(jí),更符合人性思維” 的類庫。Jackson為處理JSON格式提供了三種模型的處理方法。
1、流式API或者增量解析/產(chǎn)生( incremental parsing/generation):讀寫JSON內(nèi)容被作為離散的事件。
2、樹模型:提供一個(gè)可變內(nèi)存樹表示JSON文檔。
3、數(shù)據(jù)綁定(Data binding):實(shí)現(xiàn)JSON與POJO(簡(jiǎn)單的Java對(duì)象(Plain Old Java Object))的轉(zhuǎn)換
我們感興趣的是Java對(duì)象與JSON的轉(zhuǎn)換,因此,我們將集中于第三種處理方法。首先我們需要下載Jackson。Jackson的核心功能使用三個(gè)類庫,分別是jackson-core-2.3.1, jackson-databind-2.3.1和jackson-annotations-2.3.1; 三個(gè)類庫的下載都來自于Maven倉庫,給出地址:http://repo1.maven.org/maven2/com/fasterxml/jackson/
(譯者注:在http://repo1.maven.org/maven2/com/fasterxml/jackson/core/中,正好是三個(gè)類庫的文件夾)
現(xiàn)在,讓我們來工作吧,為了從Java對(duì)象中獲得一個(gè)一個(gè)復(fù)雜的JSON對(duì)象,我們將會(huì)使用下面的類去構(gòu)造一個(gè)對(duì)象。同樣的Java對(duì)象將會(huì)被用于這篇文章的所有的類庫中。
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對(duì)象轉(zhuǎn)換成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.."
]
}
現(xiàn)在,讓我們來看看怎么樣把Java對(duì)象轉(zhuǎn)換成JSON并且寫入文件。Jackson使用一個(gè)ObjectMapper功能,我們第一步要做的是:
Json jsonObj = new Json(); ObjectMapper mapper = new ObjectMapper();
然后,我們將會(huì)使用這個(gè)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) {
}
現(xiàn)在,我們有了一個(gè)JSON文件,但是,怎么樣轉(zhuǎn)回Java對(duì)象呢?我們可以這樣做:
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對(duì)象的相互轉(zhuǎn)換,在try-catch中總共也就兩行,看起來不錯(cuò)是吧,但是它快么?我們將會(huì)在后面的文章中揭曉。
(2)使用 Google-Gson類庫
第二種就是 Google-Gson,我們立即開始討論 Gson,你或許更喜歡他的全名Google-Gson。Gson能實(shí)現(xiàn)Java對(duì)象和JSON之間的相互轉(zhuǎn)換。甚至都不需要注釋。Gson的特點(diǎn):
1)提供簡(jiǎn)單的toJson()方法和fromJson()去實(shí)現(xiàn)相互轉(zhuǎn)換。
2)可以從JSON中轉(zhuǎn)換出之前存在的不可改變的對(duì)象。
3)擴(kuò)展提供了Java泛型。
4)支持任意復(fù)雜的對(duì)象。
Gson就需要一個(gè).jar文件,gson-2.2.4.jar,可以通過http://code.google.com/p/google-gson/downloads/list下載。下面是例子,把Java對(duì)象轉(zhuǎn)換成JSON。
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轉(zhuǎn)換成Java對(duì)象:
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) {
}
上面就是我們所有需要做的,接下來我們可以對(duì) jsonObj 作進(jìn)一步處理。當(dāng)調(diào)用JSON操作的時(shí)候,因?yàn)镚son的實(shí)例化對(duì)象沒有維持任何狀態(tài),我們可以重復(fù)使用一個(gè)對(duì)象為多個(gè)JSON序列化和反序列化操作。
(3)使用JSON-lib類庫
JSON-lib類庫是基于Douglas Crockford的工作成果,能轉(zhuǎn)換bean,map,集合(collection),java數(shù)組和XML轉(zhuǎn)換成JSON并能反向轉(zhuǎn)換成beans和動(dòng)態(tài)bean(DynaBean)。JSON-lib類庫的下載地址:http://sourceforge.net/projects/json-lib/files/ 下面這些是依賴文件
- · jakarta commons-lang 2.6
- · jakarta commons-beanutils 1.9.1
- · jakarta commons-collections 3.2.1
- · jakarta commons-logging 1.1.3
- · ezmorph 1.0.6
(譯者注:Douglas Crockford是Web開發(fā)領(lǐng)域最知名的技術(shù)權(quán)威之一,ECMA JavaScript2.0標(biāo)準(zhǔn)化委員會(huì)委員。被JavaScript之父Brendan Eich稱為JavaScript的大宗師(Yoda)。曾任Yahoo!資深JavaScript架構(gòu)師,現(xiàn)任PayPal高級(jí)JavaScript架構(gòu)師。他是JSON、JSLint、JSMin和ADSafe的創(chuàng)造者,也是名著《JavaScript: The Good Parts》(中文版《JavaScript語言精粹》)的作者。撰寫了許多廣為流傳、影響深遠(yuǎn)的技術(shù)文章,包括“JavaScript:世界上最被誤解的語言”。Douglas Crockford曾在著名的Lucasfilm電影公司任技術(shù)總監(jiān);在Paramount(派拉蒙)公司任新媒體高級(jí)總監(jiān);communities社區(qū)創(chuàng)始人兼CEO;State軟件公司CTO。2012.05.14,Paypal宣布Douglas Crockford加入Paypal。)
同樣,讓我們來把Java對(duì)象轉(zhuǎn)成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轉(zhuǎn)Java對(duì)象
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) {
}
這里有個(gè)問題,這些依賴關(guān)系會(huì)影響到性能嗎?文章在下面揭曉。
(4)使用Flexjson類庫
Flexjson是一個(gè)輕量級(jí)的庫,能序列化和反序列化Java對(duì)象和JSON,允許深層和淺層對(duì)象的拷貝。深度拷貝意味著一個(gè)被Flexjson序列化的對(duì)象,它能讓對(duì)象做到類似于延遲加載(lazy-loading)的技術(shù),能讓我們?cè)趯?duì)對(duì)象有需要時(shí)才提取。當(dāng)我們想把整個(gè)對(duì)象寫入到文件時(shí),這不是一個(gè)好的情況,但是它知道需要才去做時(shí),這是很好的。
Flexjson下載地址:http://sourceforge.net/projects/flexjson/files/ 它不需要其他庫就可以工作。下面是例子:Java對(duì)象轉(zhuǎn)JSON。
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轉(zhuǎn)Java對(duì)象
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) {
}
簡(jiǎn)單有效是吧!
(5)使用Json-io類庫
json-io有兩個(gè)主要的類,一個(gè)讀和一個(gè)寫,排除了使用ObjectInputStream和 ObjectOutputStream兩個(gè)類去讀寫。Json-io能序列化任意的Java對(duì)象圖(graph)轉(zhuǎn)變成JSON,并且能記憶完整的語義圖(graph semantics)和對(duì)象類型。下載地址: Maven Central Repository
它不需要其他依賴。
例子:Java對(duì)象轉(zhuǎn)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轉(zhuǎn)Java對(duì)象
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的序列化操作要快,我們將會(huì)在后面的文章中說明。
(6)使用Genson類庫
Genson是一個(gè)可擴(kuò)展的,可伸縮的,易于使用的開源庫。除此之外,Genson完整支持了泛型,支持JSON在JAX-RS的實(shí)現(xiàn),支持JAXB的注釋(annotation)和類型(types),并且允許序列化和反序列化擁有復(fù)雜關(guān)鍵字的map。
下載地址:http://code.google.com/p/genson/downloads/list ,它沒有任何依賴。
例子:Java對(duì)象轉(zhuǎn)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轉(zhuǎn)Java對(duì)象
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類庫
最后一個(gè)討論的是JSONiJ。JSONiJ是JSON的解析器,一個(gè)JPath和Marshaller的實(shí)現(xiàn),能實(shí)現(xiàn)Java對(duì)象和JSON的相互轉(zhuǎn)換。下載地址:https://bitbucket.org/jmarsden/jsonij/downloads
它不需要任何依賴。
例子:Java對(duì)象轉(zhuǎn)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轉(zhuǎn)Java 對(duì)象
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需要的代碼多些,性能怎么樣,我們看下面。
(二)基準(zhǔn)測(cè)試
現(xiàn)在我們要來看看性能了,測(cè)試硬件配置:Intel Core i5 laptop with 2.50GHz 單通道DDR3 RAM 4G,軟件配置:Windows 7 Ultimate 64-bit SP1
基準(zhǔn)測(cè)試運(yùn)行一樣的虛擬機(jī)(JVM),在測(cè)試之前,每一個(gè)類庫都有一個(gè)熱身,去限制內(nèi)存使用的造成的影響,用一個(gè)顯式調(diào)用垃圾收集器。下面的圖表代表的是序列化和反序列化JSON數(shù)據(jù)以毫秒級(jí)使用50次迭代和10次熱身(warm-up)迭代的平均的時(shí)間。
(譯者注:紅色為序列化(Java對(duì)象轉(zhuǎn)JSON),藍(lán)色為反序列化(JSON轉(zhuǎn)Java對(duì)象))

上面的圖表顯示,F(xiàn)lexjson序列化小數(shù)據(jù)時(shí)是最快的,而JSON-lib是最慢的。反序列化的時(shí)候,Gson最快,JSON-lib還是最慢的。
下面的圖表代表的是我們的數(shù)據(jù)在287kb時(shí),序列化和反序列化所花費(fèi)的平均時(shí)間。

這張圖顯示,我們對(duì)少量的數(shù)據(jù)操作時(shí),最快的是Gson ,之后的是 Genson和Flexjson。
當(dāng)變成大數(shù)據(jù)時(shí),結(jié)果變得很不一樣。在下面的圖表中,使用的是108Mb的數(shù)據(jù),在序列化的時(shí)候,Jackson變成了最快的,F(xiàn)lexjson變成第二快。在反序列化的時(shí)候,JSON-lib變成了最快的,之前在處理小數(shù)據(jù)時(shí),它是最慢的,第二快的是Jackson。

下面的圖表,顯示的是處理更大一點(diǎn)的數(shù)據(jù)時(shí),我們應(yīng)該使用Jackson和JSON-lib。

另外一個(gè)重要的測(cè)試是關(guān)于.jar包的大小。這對(duì)于移動(dòng)端的開發(fā)很重要,我們從下圖中看到,json-io最小,之后依次是Flexjson和JSONiJ:

(三)結(jié)論
在這篇文章中,我們知道了七種方式來實(shí)現(xiàn)Java對(duì)象和JSON之間的互相轉(zhuǎn)換。以及哪一個(gè)類庫更快,哪一個(gè)更慢,在什么情況下使用等。作為結(jié)論,如果你在你的應(yīng)用中是想使用小一點(diǎn)的數(shù)據(jù)量,你應(yīng)該使用Flexjson或者Gson,如果你需要大的數(shù)據(jù)量你應(yīng)該考慮Jackson 和JSON-lib。
