1. 程式人生 > >Android Gson解析Json(常規使用)

Android Gson解析Json(常規使用)

gson是一個java庫,用作java物件和json表示式之間的轉換。gson可以處理任何Java物件,包括已經存在的、你沒有原始碼的物件。gson有很多的優勢,比如它強大的功能:1.反序列化25M以上的字串沒有任何問題。2.它可以序列化一個有140萬個物件的集合,3.反序列話87000個物件的集合,4.gson1.4 提高了反序列化位元組陣列的限制,從87KB提高到了11M.此外。

在剛開始學習Gson的時候,我試圖在百度上尋找資料。是的,資料很多,但都是零零散散的資料,並不是我想要的系統的學習資料。瀏覽了下Gson官網,看到有個Gson User Guilde,我覺得從這裡切入是不錯的選擇,雖然我英語很爛,六級都沒過,但是我發現閱讀英文資料好像更容易讀懂一點,貌似程式設計這東西用英語表示能表示的更加具體和清晰。

1.下載Gson與例程的使用

  1.下載gson

 下載地址:點選下載gson,下載好以後把jar檔案放入libs目錄下,然後在android studio中,在jar檔案上右鍵,選擇add as library

  2.使用原始碼中的例子

  json原始碼裡面有個examples目錄,這個目錄下有個android的例子。這個例子介面有點醜,如下:

但是使用這個例子可以幫你省去一些時間,在使用這個工程的時候,注意,直接用android studio開啟後是無法使用的,但是你可以用android studio 下的 File->New->import Project功能,它會為你生成android studio可以直接執行的專案。如果這個過程出了問題,應該是gradle的某些配置不對,如果你遇到類似的問題,可以參考下我的這兩篇文章:

2 使用Gson

  為了使用Gson,首先需要獲得Gson的一個例項,有兩種方法獲得它:1. new Gson(),2. GsonBuilder,使用這個類也可以建立一個Gson例項,並且你可以做一些設定,比如版本控制等等。

2.1 new Gson()或者GsonBuilder. create()?

使用new Gson會建立一個預設引數的Gson物件,你基本不需要做什麼,家鄉下面這樣:

Gson gson = new Gson();

如果你想做細緻的控制,可以像下面這樣:

Gson gson = new GsonBuilder()  
.registerTypeAdapter(Id.class, new IdTypeAdapter())  
.enableComplexMapKeySerialization()  
.serializeNulls()  
.setDateFormat(DateFormat.LONG)  
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)  
.setPrettyPrinting()  
.setVersion(1.0)  
.create();  
Gson真的設計的非常簡練,使用Gson轉換java物件與json表示式貌似主要涉及兩個方法,而且很多情況下這個兩個方法就能搞定一切,這兩個方法就是toJson()和fromJson()。

2.2使用new Gson序列化和反序列化原始型別的例子:

<span style="font-family:SimSun;font-size:14px;">    // Serialization
    Gson gson = new Gson();
    gson.toJson(1);            // ==> 1
    gson.toJson("abcd");       // ==> "abcd"
    gson.toJson(new Long(10)); // ==> 10
    int[] values = { 1 };
    gson.toJson(values);       // ==> [1]

    // Deserialization
    int one = gson.fromJson("1", int.class);
    Integer one = gson.fromJson("1", Integer.class);
    Long one = gson.fromJson("1", Long.class);
    Boolean false = gson.fromJson("false", Boolean.class);
    String str = gson.fromJson("\"abc\"", String.class);
    String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);</span>

2.3使用new Gson序列化和反序列化一個物件

<span style="font-family:SimSun;font-size:14px;">    class BagOfPrimitives {
        private int value1 = 1;
        private String value2 = "abc";
        private transient int value3 = 3;
        BagOfPrimitives() {
            // no-args constructor
        }
    }

    // Serialization
    BagOfPrimitives obj = new BagOfPrimitives();
    Gson gson = new Gson();
    String json = gson.toJson(obj);

    // ==> json is {"value1":1,"value2":"abc"}
    // Deserialization
    BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
    // ==> obj2 is just like obj</span>
注意:你不能序列化一個迴圈引用,這將會導致無限迴圈。

  關於序列化與反序列化的物件的一些細節:

  • 推薦使用私有屬性。
  • 沒有必要使用註釋表明某個欄位將被用作序列化和反序列化,所有的欄位都會被預設的包含進來。
  • 如果一個欄位被宣告為transient,那麼它將不會被用作序列化和煩序列化。
  • 能正確處理null.
  • 序列化的時候null的欄位會被跳過。
  • 反序列化時,失蹤的條目,其物件對應的欄位會被設定位null。
  • synthetic修飾的欄位,在序列化和反序列的過程中會被忽略。
  • 內部類中對應與外部類的欄位,匿名類,本地類會被忽略。

2.4 巢狀類(nested classes)

Gson可以輕易的序列化靜態巢狀類。

Gson也可以反序列化靜態巢狀類。Gson不能自動反序列化純內部類,因為他們的無參建構函式也需要包含物件的引用,而這個引用在反序列化的時候不可用。你可以使用兩種方法解決這個問題:

  1. 宣告內部類為static
  2. 提供一個內部類的例項構建器

比如說一下例子:

public class A { 
  public String a; 

  class B { 

    public String b; 

    public B() {
      // No args constructor for B
    }
  } 
}

在這個例子中,不能使用{"b":"abc"}反序列化生成B的例項。把class B宣告位靜態是簡單的解決方法,此為,你還可以給B提供一個例項構建器:
public class InstanceCreatorForB implements InstanceCreator<A.B> {
  private final A a;
  public InstanceCreatorForB(A a)  {
    this.a = a;
  }
  public A.B createInstance(Type type) {
    return a.new B();
  }
}

這種方法雖然可行,但並不推薦。

2.5 陣列的序列化與反序列化

Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};

// Serialization
gson.toJson(ints);     // ==> [1,2,3,4,5]
gson.toJson(strings);  // ==> ["abc", "def", "ghi"]

// Deserialization
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
// ==> ints2 will be same as ints
Gson也支援多維陣列。

2.6 集合的序列化與反序列化

<span style="font-family:SimSun;font-size:14px;">Gson gson = new Gson();
Collection<Integer> ints = Lists.immutableList(1,2,3,4,5);

// Serialization
String json = gson.toJson(ints);  // ==> json is [1,2,3,4,5]

// Deserialization
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
// ==> ints2 is same as ints</span>

很可怕的一點:注意這裡是怎麼定義集合型別的?不幸的是,在java中,我們無法繞過這一點。

2.7 序列化與反序列化泛型

當我們呼叫toJson(obj),Gson會呼叫obj.getClass()來獲取需要序列化的欄位。同樣的,我們可以傳入MyClass.class物件給fromJson(json,MyClass.class),當物件不是泛型的時候,一切都工作良好,可是,如果物件是泛型,由於java型別擦除機制,泛型的資訊會丟失。下面有一個解釋這點的例子:

<span style="font-family:SimSun;font-size:14px;">class Foo<T> {
  T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // May not serialize foo.value correctly

gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar</span>
上面的程式碼無法過去Bar型別的值,所以會解析失敗。因為Gson會呼叫list.getClass()獲得類資訊,但是這個方法返回了一個原始的值,Foo.class,也就是說我們無法知道類的型別是一個Foo<Bar>型別,而不是Foo型別。

你可以解決這個問題,使用指定泛型的正確的引數化型別:

<span style="font-family:SimSun;font-size:14px;">Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);</span>

這裡通過將要過去型別的泛型型別,傳遞給一個TypeToken的匿名子類的getType方法,從而獲得一個完全的引數化型別。

2.8 序列化與反序列化任意型別的集合

有時候需要處理包含多種型別的json陣列,比如:

<span style="font-family:SimSun;font-size:14px;">{name:'GREETINGS',source:'guest'}]</span>
與之等價的集合是這樣的:
<span style="font-family:SimSun;font-size:14px;">Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));</span>

這個Event的定義是這樣的:
<span style="font-family:SimSun;font-size:14px;">class Event {
  private String name;
  private String source;
  private Event(String name, String source) {
    this.name = name;
    this.source = source;
  }
}</span>
你可以序列化集合使用toJson(connect),這沒有任何問題,可以的要期望的結果

但是,如果是使用fomJson進行反序列化的話就不能進行,因為Gson不知道怎樣把輸入對映為型別。所以你有三個選擇解決這個問題:

  1. 使用Gson的解析器API(一個低水平的流解析器或者Dom解析器JsonParser)來解析陣列元素,然後對陣列的每一個元素使用Gson.formJson()來獲得Java物件。這是推薦的方式。這裡有個例子,只貼出核心程式碼:
    <span style="font-family:SimSun;font-size:14px;">    Gson gson = new Gson();
        Collection collection = new ArrayList();
        collection.add("hello");
        collection.add(5);
        collection.add(new Event("GREETINGS", "guest"));
        String json = gson.toJson(collection);
        System.out.println("Using Gson.toJson() on a raw collection: " + json);
        JsonParser parser = new JsonParser();
        JsonArray array = parser.parse(json).getAsJsonArray();
        String message = gson.fromJson(array.get(0), String.class);
        int number = gson.fromJson(array.get(1), int.class);
        Event event = gson.fromJson(array.get(2), Event.class);
        System.out.printf("Using Gson.fromJson() to get: %s, %d, %s", message, number, event);</span>
  2. 位Collection.class註冊一個介面卡類,用來觀察每一個數組成員,並把他們對映為正確的型別。這種方法的缺點是它會搞砸Gson中其他集合型別的反序列化。
  3. 位MyCollectionMemberType註冊一個介面卡型別,並使用對Collection<MyCollectionMemberType>使用fromJson()方法。

    Gson User Guilde內容較多,這裡就只介紹它的常規使用,其他的內容會在後續的文章中繼續探究。