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轉換java物件與json表示式貌似主要涉及兩個方法,而且很多情況下這個兩個方法就能搞定一切,這兩個方法就是toJson()和fromJson()。Gson gson = new GsonBuilder() .registerTypeAdapter(Id.class, new IdTypeAdapter()) .enableComplexMapKeySerialization() .serializeNulls() .setDateFormat(DateFormat.LONG) .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) .setPrettyPrinting() .setVersion(1.0) .create();
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不能自動反序列化純內部類,因為他們的無參建構函式也需要包含物件的引用,而這個引用在反序列化的時候不可用。你可以使用兩種方法解決這個問題:
- 宣告內部類為static
- 提供一個內部類的例項構建器
比如說一下例子:
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不知道怎樣把輸入對映為型別。所以你有三個選擇解決這個問題:
- 使用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>
- 位Collection.class註冊一個介面卡類,用來觀察每一個數組成員,並把他們對映為正確的型別。這種方法的缺點是它會搞砸Gson中其他集合型別的反序列化。
- 位MyCollectionMemberType註冊一個介面卡型別,並使用對Collection<MyCollectionMemberType>使用fromJson()方法。
Gson User Guilde內容較多,這裡就只介紹它的常規使用,其他的內容會在後續的文章中繼續探究。