翻譯自JavaCreed
首先看一個(gè)Json字符串
{
'title': 'Java Puzzlers: Traps, Pitfalls, and Corner Cases',
'isbn-10': '032133678X',
'isbn-13': '978-0321336781',
'authors': ['Joshua Bloch', 'Neal Gafter']
}
如果我們不使用Gson注解@SerializeName
,那怎么來map我們Java bean類中變量的名字
比如我們的java bean類如下
public class Book{
private String[] authors;
#@SerializeName("isbn-10")
#此處我們不使用注解
private String isbn10;
#@SerializeName("isbn-13")
private String isbn13;
private String title;
// Methods removed for brevity
}
在具體講解Json反序列化之前袭厂,先了解一下Gson解析中常用的數(shù)據(jù)結(jié)構(gòu)沉眶。JsonElement,JsonPrimitive,JsonObject,JsonArray,JsonNull
還要注意的是填渠,Gson在解析過程中弦聂,是把每一個(gè)節(jié)點(diǎn)都解析成JsonElement,我們?cè)谑褂玫臅r(shí)候需要通過JsonElement的getAsJsonObject
等方法把它轉(zhuǎn)換成對(duì)應(yīng)的實(shí)際類型氛什。
下面展示一下我們自定義的deserializer莺葫,
#實(shí)現(xiàn)Gson.Deserializer接口,泛型參數(shù)是deserializer方法最終要輸出的Java Object
public class BookDeserializer implements Gson.Deserializer<Book>{
@Override
public Book deserialize(final JsonElement json,
final Type typeofT,
final JsonDeserializationContext context){
#Gson會(huì)先把輸入解析成JsonElement,
#由于我們的輸入是JsonObject,
#我們需要通過getAsJsonObject進(jìn)行轉(zhuǎn)換
JsonObject jsonObject = json.getAsJsonObject();
#記住我們得到的都是JsonElement對(duì)象枪眉,要進(jìn)行轉(zhuǎn)換
final JsonElement jsonTitle = jsonObject.get("title");
String tilte = jsonTitle.getAsString();
fina JsonArray authorsArray =
jsonObject.get("authors").getAsJsonArray();
final String[] authors = new String[authorsArray.size()];
for(int i = 0;i<authorsArray.size();i++){
authors[i] = authorsArray.get(i).getAsString();
}
final Book book = new Book();
book.setTitle(title);
book.setAuthors(authors);
return book;
}
}
接下來把我們自定義的BookDeserializer注冊(cè)給Gson
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Book.class, new BookDeserializer());
Gson gson = builder.create();
try(Reader reader = new InputStreamReader(
this.class.getResourceAsStream("books.json","utf-8"))){
Book book = gson.fromJson(reader, Book.class);
}
還考慮上面那個(gè)例子捺檬,當(dāng)嵌套的時(shí)候如何反序列化
'title': 'Java Puzzlers: Traps, Pitfalls, and Corner Cases',
'isbn-10': '032133678X',
'isbn-13': '978-0321336781',
'authors': [{"id":1,"name":"chico"},{"id":2,"name":"dong"}]
我們這里不采用最簡(jiǎn)單的使用@SerializeName常規(guī)方法,定義一個(gè)Author類贸铜,然后把Java Bean中的名字和Json中的名字對(duì)應(yīng)起來堡纬,所有工作交給Gson自動(dòng)幫我們做好。
我們?nèi)匀灰榻B自定義Deserializer的方法,這樣可控制性更強(qiáng)蒿秦,靈活度更高烤镐。
首先定義Author類的反序列化器
public class AuthorDeserializer implements JsonDeserialzier{
public Author deserialize(JsonElement element, Type typeOfT,
JsonDeserializerContext context){
JsonObject jsonObject = elemet.getAsJsonObject();
final int id = jsonObject.get("id").getAsInt();
final String name = jsonObject.get("name").getAsString();
Author author = new Author();
author.setId(id);
author.setName(name);
return author;
}
}
然后在BookDeserializer的deseralize方法中,通過第三個(gè)參數(shù)JonsDeserializerContext來代理反序列化Author棍鳖。主要就是增加如下代碼
Author[] authors = context.deserialize(jsonObject.get("authors"),Author[].class);
然后在使用的時(shí)候需要注冊(cè)這兩個(gè)反序列化器
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdpater(Book.class,new BookDeserializer());
builder.registerTypeAdapter(Author.class, new AuthorDeserializer());
Gson gson = builder.create();
接下來看一下Gson如何解析外鍵結(jié)構(gòu)炮叶,考慮如下的Json String
"authors":[
{
"id":1,
"name":"chico"
},
{
"id":2,
"name":"dong"
}
],
"books":[
{
"title":"hello",
"isbn":123456,
"authors":[1,2]
}
{
"title":"world",
"isbn":234567,
"authors":[1]
}
]
看到這個(gè)Json的結(jié)構(gòu),books包含了authors的id,類似于數(shù)據(jù)庫(kù)的外鍵镜悉,這種結(jié)構(gòu)很常見祟辟,因?yàn)槟軌蛴行У妮^少傳輸?shù)臄?shù)據(jù)量。
如何解析這種結(jié)構(gòu)呢积瞒,提供幾種思路:
1.兩段式解析川尖,首先按照J(rèn)son String的結(jié)構(gòu),解析出來相應(yīng)的Java類茫孔,這里面就是Book類和Author類叮喳,但是Book類此時(shí)并不包含Author類的引用,只包含Author的id字段缰贝。然后進(jìn)行轉(zhuǎn)換馍悟,把Book類映射到Book2類中,Book2這個(gè)類中包含了Author類的引用剩晴。這個(gè)由于需要中間的轉(zhuǎn)換锣咒,不推薦
2.另一個(gè)是在BookDeserializer類的deserialise方法中傳入Author類的信息,這樣在反序列化Book類的時(shí)候就可以直接得到相應(yīng)的Author類對(duì)象赞弥。這種想法看起來很美好毅整,但是實(shí)際上需要很大的架構(gòu)改動(dòng)才能實(shí)現(xiàn)。首先BookDeserializer和AuthorDeserializer需要共享一個(gè)Object绽左,這樣AuthorDeserializer才能把自己反序列化的結(jié)果通知BookDeserializer悼嫉。而我們?cè)诙鄠€(gè)Deserializer中間提供通信的是一個(gè)JsonDeserializerContext環(huán)境變量,這樣的話需要Gson的架構(gòu)有非常大的改動(dòng)拼窥,不推薦
3.第三種方法是讓AuthorDeserializer緩存它的解析結(jié)果戏蔑,并且對(duì)外提供通過id尋找緩存的Author的方法。這個(gè)方法改動(dòng)最小鲁纠,而且對(duì)外提供方法用到了JsonDeserializerContext总棵,也非常靈活。
先說一下第一種方法的實(shí)現(xiàn)思路
按照J(rèn)son String定義一個(gè)數(shù)據(jù)結(jié)構(gòu)
public class Data{
Author[] authors;
Book[] books;
//提供一些方法改含,根據(jù)Book中包含的id情龄,找到對(duì)應(yīng)的Author對(duì)象
//或者提供一個(gè)新類Book2,并提供把Book類轉(zhuǎn)換成Book2的方法
//Book2這個(gè)類包含了Author對(duì)象的引用
}
重點(diǎn)說一下第三種實(shí)現(xiàn)方法捍壤,第三種方法也需要一個(gè)Data類來對(duì)應(yīng)Json的結(jié)構(gòu)刃唤,不同的是不需要提供根據(jù)id來查找Author對(duì)象的方法,這個(gè)功能通過AuthorDeserializer提供白群。
接下來重寫AuthorDeserializer
public class AuthorDeserializer implements JsonDeserializer{
private static final ThreadLocal<Map<Interger,Author>> mCache
= new ThreadLocal(){
protected Map<Integer,Author> initValue(){
return new HashMap();
}
}
public Author deserialse(JsonElement elememt, Type typeOfT,
JsonDeserializerContext context) {
//如果傳進(jìn)來的是id,那么我們直接通過這個(gè)Id去尋找對(duì)應(yīng)的Author對(duì)象
if(element.isJsonPrimitive()){
final JsonPrimitive primitive = element.getAsJsonPrimitive();
//核心方法硬霍,通過id尋找已經(jīng)緩存的Author對(duì)象或者創(chuàng)建Author對(duì)應(yīng)并緩存
return getOrCreate(primitive.getAsInt());
}
//如果傳進(jìn)來的是整個(gè)Author的Json String,也去創(chuàng)建或緩存
if(element.isJsonObject){
final JsonObject jsonObject = element.getAsJsonObject();
final Author author = getOrCreate(jsonObject.get("id").getAsInt());
author.setName(jsonObject.get("name").getAsString);
return author;
}
throw new JsonParseException("Unexpected JSON type: " + json.getClass().getSimpleName());
}
private Author getOrCreate(int id){
Author author = mCache.get().get(id);
if(author == null){
author = new Author();
author.setId(id);
mCache.get().put(id,author);
}
return author;
}
}
講一下改動(dòng)帜慢,我們定義了一個(gè)ThreadLocal<Map<Integer,Author>>類型的變量作為Cache,這樣可以保證每一個(gè)線程都有一個(gè)獨(dú)立的Map<Integer,Author>副本.
所有獲取Author實(shí)例的操作,都由getOrCreate來獲得粱玲,getOrCreate的巧妙之處在于它僅僅根據(jù)id來生成Author躬柬,等到真正需要Author的時(shí)候,在根據(jù)Json String中的內(nèi)容填入name等字段抽减,這樣就不用擔(dān)心Book和Author哪個(gè)先被反序列化了允青。如果Book先被反序列化,他得到的Author對(duì)象是只包含id的Author對(duì)象卵沉,等Author真正被反序列化的時(shí)候剩余的字段會(huì)被填上颠锉。如果是Author對(duì)象先被反序列化,那么直接可以得到完整的Author對(duì)象史汗。
然后再看一下deserialize方法的變化琼掠,如果我們?cè)贐ookDeserializer中執(zhí)行
Author[] authors = context.deserialise(jsonObject.get("authors"),Author[].class);
這樣在AuthorDeserializer的deserialize方法中就會(huì)走第一個(gè)if,也就是
if(element.isJsonPrimitive()){
}
當(dāng)真正的Author結(jié)構(gòu)的Json String來的時(shí)候會(huì)走第二個(gè)if,也就是
if(element.isJsonObject()){
}
在這里我們根據(jù)Json String來補(bǔ)全對(duì)應(yīng)的Author信息。
好了停撞,反序列化最常見的問題已經(jīng)介紹完了瓷蛙。