Gson的使用--反序列化

翻譯自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)介紹完了瓷蛙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市戈毒,隨后出現(xiàn)的幾起案子艰猬,更是在濱河造成了極大的恐慌,老刑警劉巖埋市,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冠桃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡恐疲,警方通過查閱死者的電腦和手機(jī)腊满,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來培己,“玉大人碳蛋,你說我怎么就攤上這事∈∽桑” “怎么了肃弟?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)零蓉。 經(jīng)常有香客問我笤受,道長(zhǎng),這世上最難降的妖魔是什么敌蜂? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任箩兽,我火速辦了婚禮,結(jié)果婚禮上章喉,老公的妹妹穿的比我還像新娘汗贫。我一直安慰自己身坐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布落包。 她就那樣靜靜地躺著部蛇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咐蝇。 梳的紋絲不亂的頭發(fā)上涯鲁,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天剩盒,我揣著相機(jī)與錄音唆鸡,去河邊找鬼摔蓝。 笑死破停,一個(gè)胖子當(dāng)著我的面吹牛烤蜕,可吹牛的內(nèi)容都是我干的卖子。 我是一名探鬼主播斩萌,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼玻褪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼许师!你這毒婦竟也來了房蝉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤微渠,失蹤者是張志新(化名)和其女友劉穎搭幻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逞盆,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡檀蹋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了云芦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俯逾。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖舅逸,靈堂內(nèi)的尸體忽然破棺而出桌肴,到底是詐尸還是另有隱情,我是刑警寧澤琉历,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布坠七,位于F島的核電站,受9級(jí)特大地震影響旗笔,放射性物質(zhì)發(fā)生泄漏彪置。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一蝇恶、第九天 我趴在偏房一處隱蔽的房頂上張望拳魁。 院中可真熱鬧,春花似錦撮弧、人聲如沸潘懊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卦尊。三九已至,卻和暖如春舌厨,著一層夾襖步出監(jiān)牢的瞬間岂却,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工裙椭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躏哩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓揉燃,卻偏偏與公主長(zhǎng)得像扫尺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子炊汤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理正驻,服務(wù)發(fā)現(xiàn),斷路器抢腐,智...
    卡卡羅2017閱讀 134,714評(píng)論 18 139
  • 1.概述2.Gson的目標(biāo)3.Gson的性能和擴(kuò)展性4.Gson的使用者5.如何使用Gson 通過Maven來使用...
    人失格閱讀 14,268評(píng)論 2 18
  • 為了更好的學(xué)習(xí)Gson姑曙,特將Gson User Guide翻譯如下。由于本人英文水平有限迈倍,如有錯(cuò)誤伤靠,還請(qǐng)指正,謝謝...
    WeberLisper閱讀 6,843評(píng)論 0 6
  • Android中Gson的使用 1 簡(jiǎn)介 Gson是一個(gè)Java庫(kù)啼染,作用是將Java對(duì)象轉(zhuǎn)換成它對(duì)應(yīng)的JSON表示...
    LuckyXiang閱讀 20,146評(píng)論 2 3
  • 姓名:黎文淵 學(xué)號(hào):17021210909 本文轉(zhuǎn)自 https://www.sohu.com/a/205934...
    顏淵_d001閱讀 295評(píng)論 0 0