【轉載】Android Gson使用詳解

Json 是一種文本形式的數據交換格式,比 xml 更爲輕量。Json 的解析和生成的方式很多,在 Android 平臺上最常用的類庫有 Gson 和 FastJson 兩種,這裏要介紹的是 Gson

Gson 的 GitHub 主頁點擊這裏:Gson

一、Gson的基本用法

1.1、Gson對象

在進行序列化與反序列操作前,需要先實例化一個 com .google.gson.Gson 對象,獲取 Gson 對象的方法有兩種

        //通過構造函數來獲取
        Gson gson = new Gson();
        //通過 GsonBuilder 來獲取,可以進行多項特殊配置
        Gson gson = new GsonBuilder().create();

1.2、生成 Json

利用 Gson 可以很方便地生成 Json 字符串,通過使用 addProperty 的四個重載方法

    public static void main(String[] args) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("String", "leavesC");
        jsonObject.addProperty("Number_Integer", 23);
        jsonObject.addProperty("Number_Double", 22.9);
        jsonObject.addProperty("Boolean", true);
        jsonObject.addProperty("Char", 'c');
        System.out.println();
        System.out.println(jsonObject);
    }

addProperty 方法底層調用的是 add(String property, JsonElement value) 方法,即將基本數據類型轉化爲了 JsonElement 對象,JsonElement 是一個抽象類,而 JsonObject 繼承了 JsonElement ,因此我們可以通過 JsonObject 自己來構建一個 JsonElement

    public static void main(String[] args) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("String", "leavesC");
        jsonObject.addProperty("Number", 23);
        jsonObject.addProperty("Number", 22.9);
        jsonObject.addProperty("Boolean", true);
        jsonObject.addProperty("Char", 'c');
    JsonObject jsonElement = <span class="hljs-keyword">new</span> JsonObject();
    jsonElement.addProperty(<span class="hljs-string">"Boolean"</span>, <span class="hljs-literal">false</span>);
    jsonElement.addProperty(<span class="hljs-string">"Double"</span>, <span class="hljs-number">25.9</span>);
    jsonElement.addProperty(<span class="hljs-string">"Char"</span>, <span class="hljs-string">'c'</span>);
    jsonObject.add(<span class="hljs-string">"JsonElement"</span>, jsonElement);

    System.out.println();
    System.out.println(jsonObject);
}

1.3、Json與數組、List的轉化

Json數組 與 字符串數組

    public static void main(String[] args) {
        //Json數組 轉爲 字符串數組
        Gson gson = new Gson();
        String jsonArray = "[\"https://github.com/leavesC\",\"https://www.jianshu.com/u/9df45b87cfdf\",\"Java\",\"Kotlin\",\"Git\",\"GitHub\"]";
        String[] strings = gson.fromJson(jsonArray, String[].class);
        System.out.println("Json數組 轉爲 字符串數組: ");
        for (String string : strings) {
            System.out.println(string);
        }
        //字符串數組 轉爲 Json數組
        jsonArray = gson.toJson(strings, String[].class);
        System.out.println("\n字符串數組 轉爲 Json數組: ");
        System.out.println(jsonArray);
    }

Json數組 與 List

    public static void main(String[] args) {
        //Json數組 轉爲 List
        Gson gson = new Gson();
        String jsonArray = "[\"https://github.com/leavesC\",\"https://www.jianshu.com/u/9df45b87cfdf\",\"Java\",\"Kotlin\",\"Git\",\"GitHub\"]";
        List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {
        }.getType());
        System.out.println("\nJson數組 轉爲 List: ");
        for (String string : stringList) {
            System.out.println(string);
        }
        //List 轉爲 Json數組
        jsonArray = gson.toJson(stringList, new TypeToken<List<String>>() {
        }.getType());
        System.out.println("\nList 轉爲 Json數組: ");
        System.out.println(jsonArray);
    }

1.4、序列化與反序列化

Gson 也提供了 toJson()fromJson() 兩個方法用於轉化 Model 與 Json,前者實現了序列化,後者實現了反序列化
首先,聲明一個 User 類

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class User {
<span class="hljs-keyword">private</span> String name;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> age;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> sex;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">User</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age, <span class="hljs-keyword">boolean</span> sex)</span> </span>{
    <span class="hljs-keyword">this</span>.name = name;
    <span class="hljs-keyword">this</span>.age = age;
    <span class="hljs-keyword">this</span>.sex = sex;
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"User{"</span> +
            <span class="hljs-string">"name='"</span> + name + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", age="</span> + age +
            <span class="hljs-string">", sex="</span> + sex +
            <span class="hljs-string">'}'</span>;
}

}

序列化的方法很簡單,調用 gson 對象的 toJson 方法,傳入要序列化的對象

    public static void main(String[] args) {
        //序列化
        User user = new User("leavesC", 24, true);
        Gson gson = new Gson();
        System.out.println();
        System.out.println(gson.toJson(user));
    }

反序化的方式也類似

    public static void main(String[] args) {
        //反序列化
        String userJson = "{\"name\":\"leavesC\",\"age\":24,\"sex\":true}";
        Gson gson = new Gson();
        User user = gson.fromJson(userJson, User.class);
        System.out.println();
        System.out.println(user);
    }

二、屬性重命名

繼續使用上一節聲明的 User 類,根據 User 類聲明的各個屬性名,移動端的開發者希望接口返回的數據格式即是如下這樣的

{"name":"leavesC","age":24,"sex":true}

如果沒有和服務器端溝通好或者是 API 改版了,接口返回的數據格式可能是這樣的

{"Name":"leavesC","age":24,"sex":true}
{"userName":"leavesC","age":24,"sex":true}

如果繼續使用上一節介紹的方法,那無疑會解析出錯
例如

    public static void main(String[] args) {
        //反序列化
        String userJson = "{\"userName\":\"leavesC\",\"age\":24,\"sex\":true}";
        Gson gson = new Gson();
        User user = gson.fromJson(userJson, User.class);
        System.out.println();
        System.out.println(user);
    }

name 屬性值解析不到,所以爲 null


此時爲了兼顧多種格式的數據,就需要使用 SerializedName 註解
根據 SerializedName 的聲明來看,SerializedName 包含兩個屬性值,一個是字符串,一個是字符串數組,而字符串數組含有默認值

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface SerializedName {
    String value();
<span class="hljs-selector-tag">String</span><span class="hljs-selector-attr">[]</span> <span class="hljs-selector-tag">alternate</span>() <span class="hljs-selector-tag">default</span> {};

}

SerializedName 的作用是爲了在序列化或反序列化時,指導 Gson 如果將原有的屬性名和其它特殊情況下的屬性名聯繫起來

例如,修改 User 類,爲 name 聲明 SerializedName 註解,註解值爲 userName

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class User {
<span class="hljs-meta">@SerializedName</span>(<span class="hljs-string">"userName"</span>)
<span class="hljs-keyword">private</span> String name;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> age;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> sex;

}

在序列時,Json 格式就會相應改變

    public static void main(String[] args) {
        //序列化
        User user = new User("leavesC", 24, true);
        Gson gson = new Gson();
        System.out.println();
        System.out.println(gson.toJson(user));
    }

在反序列化時也一樣,能夠解析到正確的屬性值

    public static void main(String[] args) {
        //反序列化
        String userJson = "{\"userName\":\"leavesC\",\"age\":24,\"sex\":true}";
        Gson gson = new Gson();
        User user = gson.fromJson(userJson, User.class);
        System.out.println();
        System.out.println(user);
    }

還有個問題沒解決,爲了應對多種屬性名不一致的情況,難道我們要聲明多個 User 類嗎?這顯然是不現實的,所以還需要爲 User 類設置多個備選屬性名,這就需要用到 SerializedName 註解的另一個屬性值 alternate 了。

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class User {
<span class="hljs-meta">@SerializedName</span>(value = <span class="hljs-string">"userName"</span>, alternate = {<span class="hljs-string">"user_name"</span>, <span class="hljs-string">"Name"</span>})
<span class="hljs-keyword">private</span> String name;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> age;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> sex;

}

以下幾種情況都能夠被正確的反序列化

    public static void main(String[] args) {
        //反序列化
        Gson gson = new Gson();
        String userJson = "{\"userName\":\"leavesC\",\"age\":24,\"sex\":true}";
        User user = gson.fromJson(userJson, User.class);
        System.out.println();
        System.out.println(user);
    userJson = <span class="hljs-string">"{\"user_name\":\"leavesC\",\"age\":24,\"sex\":true}"</span>;
    user = gson.fromJson(userJson, User.class);
    System.out.println();
    System.out.println(user);

    userJson = <span class="hljs-string">"{\"Name\":\"leavesC\",\"age\":24,\"sex\":true}"</span>;
    user = gson.fromJson(userJson, User.class);
    System.out.println();
    System.out.println(user);
}

三、字段過濾

有時候並不是所有的字段都需要進行系列化和反序列化,因此需要對某些字段進行排除,有四種方法可以來實現這種需求。

3.1、基於@Expose註解

Expose 註解包含兩個屬性值,且均聲明瞭默認值。Expose 的含義即爲“暴露”,即用於對外暴露字段,serialize 用於指定是否進行序列化,deserialize 用於指定是否進行反序列化。如果字段不聲明 Expose 註解,則意味着不進行序列化和反序列化操作,相當於兩個屬性值均爲 false 。此外,Expose 註解需要和 GsonBuilder 構建的 Gson 對象一起使用才能生效。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Expose {
    boolean serialize() default true;
<span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">deserialize</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">true</span></span>;

}

Expose 註解的註解值聲明情況有四種

    @Expose(serialize = true, deserialize = true)   //序列化和反序列化都生效
    @Expose(serialize = false, deserialize = true)  //序列化時不生效,反序列化時生效
    @Expose(serialize = true, deserialize = false)  //序列化時生效,反序列化時不生效
    @Expose(serialize = false, deserialize = false) //序列化和反序列化都不生效,和不寫註解一樣

現在來看個例子,修改 User 類

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class User {
<span class="hljs-meta">@Expose</span>(serialize = <span class="hljs-keyword">true</span>, deserialize = <span class="hljs-keyword">true</span>)   <span class="hljs-comment">//序列化和反序列化都生效</span>
<span class="hljs-keyword">private</span> String a;

<span class="hljs-meta">@Expose</span>(serialize = <span class="hljs-keyword">false</span>, deserialize = <span class="hljs-keyword">true</span>)  <span class="hljs-comment">//序列化時不生效,反序列化時生效</span>
<span class="hljs-keyword">private</span> String b;

<span class="hljs-meta">@Expose</span>(serialize = <span class="hljs-keyword">true</span>, deserialize = <span class="hljs-keyword">false</span>)  <span class="hljs-comment">//序列化時生效,反序列化時不生效</span>
<span class="hljs-keyword">private</span> String c;

<span class="hljs-meta">@Expose</span>(serialize = <span class="hljs-keyword">false</span>, deserialize = <span class="hljs-keyword">false</span>) <span class="hljs-comment">//序列化和反序列化都不生效,和不寫註解一樣</span>
<span class="hljs-keyword">private</span> String d;

<span class="hljs-keyword">private</span> String e;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">User</span><span class="hljs-params">(String a, String b, String c, String d, String e)</span> </span>{
    <span class="hljs-keyword">this</span>.a = a;
    <span class="hljs-keyword">this</span>.b = b;
    <span class="hljs-keyword">this</span>.c = c;
    <span class="hljs-keyword">this</span>.d = d;
    <span class="hljs-keyword">this</span>.e = e;
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"User{"</span> +
            <span class="hljs-string">"a='"</span> + a + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", b='"</span> + b + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", c='"</span> + c + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", d='"</span> + d + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", e='"</span> + e + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">'}'</span>;
}

}

按照如上的註解值,只有聲明瞭 Expose 註解且 serialize 值爲 true 的字段才能被序列化,只有聲明瞭 Expose 註解且 deserialize 值爲 true 的字段才能被反序列化

    public static void main(String[] args) {
        Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
        User user = new User("A", "B", "C", "D", "E");
        System.out.println();
        System.out.println(gson.toJson(user));
    String json = <span class="hljs-string">"{\"a\":\"A\",\"b\":\"B\",\"c\":\"C\",\"d\":\"D\",\"e\":\"E\"}"</span>;
    user = gson.fromJson(json, User.class);
    System.out.println();
    System.out.println(user.toString());
}

3.2、基於版本

Gson 提供了 @Since 和 @Until 兩個註解基於版本對字段進行過濾,@Since 和 @Until 都包含一個 Double 屬性值,用於設置版本號。Since 的意思是“自……開始”,Until 的意思是“到……爲止”,一樣要和 GsonBuilder 配合使用。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
    double value();
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {
double value();
}

當版本( GsonBuilder 設置的版本) 大於或等於 Since 屬性值或小於 Until 屬性值時字段會進行序列化和反序列化操作,而沒有聲明註解的字段都會加入序列化和反序列操作

現在來看個例子,修改 User 類

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class User {
<span class="hljs-meta">@Since</span>(<span class="hljs-number">1.4</span>)
<span class="hljs-keyword">private</span> String a;

<span class="hljs-meta">@Since</span>(<span class="hljs-number">1.6</span>)
<span class="hljs-keyword">private</span> String b;

<span class="hljs-meta">@Since</span>(<span class="hljs-number">1.8</span>)
<span class="hljs-keyword">private</span> String c;

<span class="hljs-meta">@Until</span>(<span class="hljs-number">1.6</span>)
<span class="hljs-keyword">private</span> String d;

<span class="hljs-meta">@Until</span>(<span class="hljs-number">2.0</span>)
<span class="hljs-keyword">private</span> String e;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">User</span><span class="hljs-params">(String a, String b, String c, String d, String e)</span> </span>{
    <span class="hljs-keyword">this</span>.a = a;
    <span class="hljs-keyword">this</span>.b = b;
    <span class="hljs-keyword">this</span>.c = c;
    <span class="hljs-keyword">this</span>.d = d;
    <span class="hljs-keyword">this</span>.e = e;
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"User{"</span> +
            <span class="hljs-string">"a='"</span> + a + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", b='"</span> + b + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", c='"</span> + c + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", d='"</span> + d + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", e='"</span> + e + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">'}'</span>;
}

}

    public static void main(String[] args) {
        Gson gson = new GsonBuilder().setVersion(1.6).create();
        User user = new User("A", "B", "C", "D", "E");
        System.out.println();
        System.out.println(gson.toJson(user));

        String json = "{\"a\":\"A\",\"b\":\"B\",\"c\":\"C\",\"d\":\"D\",\"e\":\"E\"}";
        user = gson.fromJson(json, User.class);
        System.out.println();
        System.out.println(user.toString());
    }

3.3、基於訪問修飾符

訪問修飾符由 java.lang.reflect.Modifier 提供 int 類型的定義,而 GsonBuilder 對象的 excludeFieldsWithModifiers方法接收一個 int 類型可變參數,指定不進行序列化和反序列化操作的訪問修飾符字段
看個例子

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class ModifierSample {
public <span class="hljs-built_in">String</span> publicField = <span class="hljs-string">"public"</span>;

protected <span class="hljs-built_in">String</span> protectedField = <span class="hljs-string">"protected"</span>;

private <span class="hljs-built_in">String</span> privateField = <span class="hljs-string">"private"</span>;

<span class="hljs-built_in">String</span> defaultField = <span class="hljs-string">"default"</span>;

final <span class="hljs-built_in">String</span> finalField = <span class="hljs-string">"final"</span>;

<span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> staticField = <span class="hljs-string">"static"</span>;

}

    public static void main(String[] args) {
        Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.STATIC).create();
        ModifierSample modifierSample = new ModifierSample();
        System.out.println(gson.toJson(modifierSample));
    }

3.4、基於策略

GsonBuilder 類包含 setExclusionStrategies(ExclusionStrategy... strategies)方法用於傳入不定長參數的策略方法,用於直接排除指定字段名或者指定字段類型
看個例子

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class Strategies {
<span class="hljs-keyword">private</span> String stringField;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> intField;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">double</span> doubleField;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Strategies</span><span class="hljs-params">(String stringField, <span class="hljs-keyword">int</span> intField, <span class="hljs-keyword">double</span> doubleField)</span> </span>{
    <span class="hljs-keyword">this</span>.stringField = stringField;
    <span class="hljs-keyword">this</span>.intField = intField;
    <span class="hljs-keyword">this</span>.doubleField = doubleField;
}

@<span class="hljs-function">Override
<span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Strategies{"</span> +
            <span class="hljs-string">"stringField='"</span> + stringField + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", intField="</span> + intField +
            <span class="hljs-string">", doubleField="</span> + doubleField +
            <span class="hljs-string">'}'</span>;
}

}

public static void main(String[] args) {
        Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                //排除指定字段名
                return fieldAttributes.getName().equals("intField");
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                //排除指定字段類型
                return aClass.getName().equals(double.class.getName());
            }
        }).create();

        Strategies strategies = new Strategies("stringField", 111, 11.22);
        System.out.println();
        System.out.println(gson.toJson(strategies));

        String json = "{\"stringField\":\"stringField\",\"intField\":111,\"doubleField\":11.22}";
        strategies = gson.fromJson(json, Strategies.class);
        System.out.println();
        System.out.println(strategies);
    }

字段名爲 "intField" 和字段類型爲 double 的字段都會被排除掉


setExclusionStrategies 方法在序列化和反序列化時都會生效,如果只是想指定其中一種情況下的排除策略或分別指定排除策略,可以改爲使用以下兩個方法

addSerializationExclusionStrategy(ExclusionStrategy strategy);

addDeserializationExclusionStrategy(ExclusionStrategy strategy);

四、個性化配置

4.1、輸出 null

對於 Gson 而言,在序列化時如果某個屬性值爲 null 的話,那麼在序列化時該字段不會參與進來,如果想要顯示輸出該字段的話,可以通過 GsonBuilder 進行配置

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class Strategies {
<span class="hljs-keyword">private</span> String stringField;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> intField;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">double</span> doubleField;

}

 public static void main(String[] args) {
        Gson gson = new GsonBuilder()
                .serializeNulls() //輸出null
                .create();
        Strategies strategies = new Strategies(null, 24, 22.333);
        System.out.println();
        System.out.println(gson.toJson(strategies));
 }

4.2、格式化輸出Json

默認的序列化後的 Josn 字符串並不太直觀,可以選擇格式化輸出

    public static void main(String[] args) {
        Gson gson = new GsonBuilder()
                .serializeNulls() //輸出null
                .setPrettyPrinting()//格式化輸出
                .create();
        Strategies strategies = new Strategies(null, 24, 22.333);
        System.out.println();
        System.out.println(gson.toJson(strategies));
    }

4.3、格式化時間

Gson 也可以對時間值進行格式化

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class Strategies {
<span class="hljs-keyword">private</span> Date date;

<span class="hljs-keyword">private</span> Date date2;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Strategies</span><span class="hljs-params">(Date date, Date date2)</span> </span>{
    <span class="hljs-keyword">this</span>.date = date;
    <span class="hljs-keyword">this</span>.date2 = date2;
}

@<span class="hljs-function">Override
<span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
    SimpleDateFormat simpleDateFormat = <span class="hljs-keyword">new</span> SimpleDateFormat(<span class="hljs-string">"yyyy-MM-dd HH:mm:ss:SSS"</span>, Locale.CHINA);
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Strategies{"</span> +
            <span class="hljs-string">"date="</span> + simpleDateFormat.format(date) +
            <span class="hljs-string">", date2="</span> + simpleDateFormat.format(date2) +
            <span class="hljs-string">'}'</span>;
}

}

public static void main(String[] args) {
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()//格式化輸出
                .setDateFormat("yyyy-MM-dd HH:mm:ss:SSS")//格式
化時間
                .create();
        Date date = new Date();
        Strategies strategies = new Strategies(date, new Date(date.getTime() + 1000000));
        System.out.println();
        System.out.println(gson.toJson(strategies));

        String json = "{\n" +
                "  \"date\": \"2018-03-17 19:38:50:033\",\n" +
                "  \"date2\": \"2018-03-17 19:55:30:033\"\n" +
                "}";
        System.out.println();
        System.out.println(gson.fromJson(json, Strategies.class));
    }

五、TypeAdapter

TypeAdapter 是一個泛型抽象類,用於接管某種類型的序列化和反序列化過程,包含兩個抽象方法,分別用於自定義序列化和反序列化過程

public abstract void write(JsonWriter var1, T var2) throws IOException;

public abstract T read(JsonReader var1) throws IOException;

下面看個簡單的例子

/**
 * 作者:chenZY
 * 時間:2018/3/17 18:32
 * 描述:https://github.com/leavesC
 */
public class User {
<span class="hljs-keyword">private</span> String name;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> age;

<span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> sex;

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">User</span><span class="hljs-params">()</span> </span>{
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">User</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age, <span class="hljs-keyword">boolean</span> sex)</span> </span>{
    <span class="hljs-keyword">this</span>.name = name;
    <span class="hljs-keyword">this</span>.age = age;
    <span class="hljs-keyword">this</span>.sex = sex;
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"User{"</span> +
            <span class="hljs-string">"name='"</span> + name + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", age="</span> + age +
            <span class="hljs-string">", sex="</span> + sex +
            <span class="hljs-string">'}'</span>;
}

}

定義 TypeAdapter 的子類 UserTypeAdapter 來接管 User 類的序列化和反序列化過程
這裏設定當 User 類序列化時 Json 中的Key值都是大寫字母開頭,反序列化時支持“name”和“Name”兩種不同的 Json 風格

public class UserTypeAdapter extends TypeAdapter<User> {
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">write</span><span class="hljs-params">(JsonWriter jsonWriter, User user)</span> <span class="hljs-keyword">throws</span> IOException </span>{
    <span class="hljs-comment">//流式序列化成對象開始</span>
    jsonWriter.beginObject();
    <span class="hljs-comment">//將Json的Key值都指定爲大寫字母開頭</span>
    jsonWriter.name(<span class="hljs-string">"Name"</span>).value(user.getName());
    jsonWriter.name(<span class="hljs-string">"Age"</span>).value(user.getAge());
    jsonWriter.name(<span class="hljs-string">"Sex"</span>).value(user.isSex());
    <span class="hljs-comment">//流式序列化結束</span>
    jsonWriter.endObject();
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">read</span><span class="hljs-params">(JsonReader jsonReader)</span> <span class="hljs-keyword">throws</span> IOException </span>{
    User user = <span class="hljs-keyword">new</span> User();
    <span class="hljs-comment">//流式反序列化開始</span>
    jsonReader.beginObject();
    <span class="hljs-keyword">while</span> (jsonReader.hasNext()) {
        <span class="hljs-keyword">switch</span> (jsonReader.nextName()) {
            <span class="hljs-comment">//首字母大小寫均合法</span>
            <span class="hljs-keyword">case</span> <span class="hljs-string">"name"</span>:
            <span class="hljs-keyword">case</span> <span class="hljs-string">"Name"</span>:
                user.setName(jsonReader.nextString());
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">"age"</span>:
                user.setAge(jsonReader.nextInt());
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">"sex"</span>:
                user.setSex(jsonReader.nextBoolean());
                <span class="hljs-keyword">break</span>;
        }

    }
    <span class="hljs-comment">//流式反序列化結束</span>
    jsonReader.endObject();
    <span class="hljs-keyword">return</span> user;
}

}

    public static void main(String[] args) {
        Gson gson = new GsonBuilder().registerTypeAdapter(User.class, new UserTypeAdapter()).create();
        User user = new User("leavesC", 24, true);
        System.out.println();
        System.out.println(gson.toJson(user));

        String json = "{\"Name\":\"leavesC\",\"age\":24,\"sex\":true}";
        user = gson.fromJson(json, User.class);
        System.out.println();
        System.out.println(user);
    }

可以看到 User 類按照預定義的策略來完成序列化和反序列化了


六、JsonSerializer 和 JsonDeserializer

TypeAdapter 將序列化和反序列操作都接管了過來,其實 Gson 還提供了只接管序列化過程的接口,即 JsonSerializer
看個例子

    public static void main(String[] args) {
        Gson gson = new GsonBuilder().registerTypeAdapter(User.class, new JsonSerializer<User>() {
            @Override
            public JsonElement serialize(User user, Type type, JsonSerializationContext jsonSerializationContext) {
                JsonObject jsonObject = new JsonObject();
                jsonObject.addProperty("NameHi", user.getName());
                jsonObject.addProperty("Sex", user.isSex());
                jsonObject.addProperty("Age", user.getAge());
                return jsonObject;
            }
        }).create();
        User user = new User("leavesC", 24, true);
        System.out.println();
        System.out.println(gson.toJson(user));
    }

相對應的,JsonDeserializer 接口提供了反序列化的接口

public static void main(String[] args) {
        Gson gson = new GsonBuilder().registerTypeAdapter(User.class, new JsonDeserializer<User>() {
            @Override
            public User deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
                JsonObject jsonObject = jsonElement.getAsJsonObject();
                String name = null;
                //同時支持 userName 和 name 兩種情況
                if (jsonObject.has("userName")) {
                    name = jsonObject.get("userName").getAsString();
                } else if (jsonObject.has("name")) {
                    name = jsonObject.get("name").getAsString();
                }
                int age = jsonObject.get("age").getAsInt();
                boolean sex = jsonObject.get("sex").getAsBoolean();
                return new User(name, age, sex);
            }
        }).create();
        String json = "{\"userName\":\"leavesC\",\"sex\":true,\"age\":24}";
        User user = gson.fromJson(json, User.class);
        System.out.println();
        System.out.println(user);
    json = <span class="hljs-string">"{\"name\":\"leavesC\",\"sex\":true,\"age\":24}"</span>;
    user = gson.fromJson(json, User.class);
    System.out.println();
    System.out.println(user);
}

這裏有個比較麻煩的地方,那就是在使用 TypeAdapter 、JsonSerializerJsonDeserializer 時,總需要調用 registerTypeAdapter 方法進行註冊,那有沒有更簡單的註冊方法呢?
有的,Gosn 還提供了另一個註解 @JsonAdapter 用於進行簡單的聲明

類似於這樣,聲明瞭 User 類的序列化或反序列化操作由 UserTypeAdapter 完成,註解的優先級高於 registerTypeAdapter 方法

@JsonAdapter(UserTypeAdapter.class)
public class User {

}

七、TypeAdapterFactory

TypeAdapterFactory 是用於創建 TypeAdapter 的工廠類,通過參數 TypeToken 來查找確定對應的 TypeAdapter,如果沒有就返回 null 並由 Gson 默認的處理方法來進行序列化和反序列化操作,否則就由用戶預定義的 TypeAdapter 來進行處理

        Gson gson = new GsonBuilder().registerTypeAdapterFactory(new TypeAdapterFactory() {
            @Override
            public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
                //如果 Gson 需要與 User 類相關的 TypeAdapter ,則返回我們自己定義的 TypeAdapter 對象
                if (typeToken.getType().getTypeName().equals(User.class.getTypeName())) {
                    return (TypeAdapter<T>) new UserTypeAdapter();
                }
                //找不到則返回null
                return null;
            }
        }).create();

八、結語

這一篇文章好像寫得太長了一點?Gson 的知識點介紹到這裏也差不多了,以後如果還發現新內容的話我會繼續補充,現在就先這樣啦
此外,可以來簡書關注我:葉應是葉 -> https://www.jianshu.com/u/9df45b87cfdf

還有我的 GitHub 主頁: leavesC -> https://github.com/leavesC

      </div>
    </div>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章