Gson?So easy.

1.概述

這篇文章主要講述了Gson的使用.包括從最基礎的基本類型的序列化,到對象,數組,集合,再到Gson註解,Gson Builder,再到格式化,自定義序列化與反序列化等內容.
另外文章篇幅較長,建議挑選所需部分查看.所有例子都提供了完整源碼,在文章的後面.

2.Gson是什麼?

(1)JSON

JSON全稱爲JavaScript Object Notation,一種輕量級的數據交換格式.

類似於XML但比XML更小,更易解析.

(2)Gson

Gson是Google提供的可以使Java對象與JSON互轉的類庫,可將Java對象轉換爲JSON,也可將JSON轉換成Java對象.

(3)Gson的好處

  • a.容易,高效,強大:Gson是Google管理的標準化庫,經過高度優化,同時api簡單,比如fromJSON(),toJSON().
  • b.無依賴性:不需要其他庫,當然jdk除外.
  • c.結果簡單:轉換成的json易於閱讀.
  • d.支持泛型,支持內部類.
  • e.開源,免費提供.

3.配置Gson環境

目前最新的是2.8.6版本.

(1)Gradle

dependencies {
  implementation 'com.google.code.gson:gson:2.8.6'
}

(2)Maven

<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.8.6</version>
</dependency>

(3)jar

點擊下載.

4.Gson基礎

(1)基礎類型

int intJSON = 1;
long longJSON = 1L;
double doubleJSON = 1.0;
float floatJSON = 1.0f;
byte byteJSON = 1;
char charJSON = 'G';
short shortJSON = 1;
boolean boooleanJSON = true;
System.out.println(gson.toJson(intJSON));
System.out.println(gson.toJson(longJSON));
System.out.println(gson.toJson(doubleJSON));
System.out.println(gson.toJson(floatJSON));
System.out.println(gson.toJson(byteJSON));
System.out.println(gson.toJson(charJSON));
System.out.println(gson.toJson(shortJSON));
System.out.println(gson.toJson(boooleanJSON));

System.out.println("----------------------------------------");

System.out.println(gson.fromJson("1",Integer.class));
System.out.println(gson.fromJson("1.0",Double.class));
System.out.println(gson.fromJson("1",Long.class));
System.out.println(gson.fromJson("true",Boolean.class));
System.out.println(gson.fromJson("B",Character.class));

在這裏插入圖片描述
就是對應輸出,沒啥好說的.

(2)嵌套對象

Gson gson = new Gson();
System.out.println(gson.toJson(new A()));

String str = "{'field':'gggg','field2':33,'field3':'G','field4':'true'}";
A a = gson.fromJson(str,A.class);

System.out.println(a.getField1());
System.out.println(a.getField2());
System.out.println(a.getField3());
System.out.println(a.getField4());

A類:

class A
{
    private String field1 = "123";
    private int field2 = 1;
    private char field3 = 'X';
    private Boolean field4 = true;
}

在這裏插入圖片描述
序列化出來的對象用{}表示.

反序列化時,注意格式,注意名字對應,用單引號引起來,還有char會自動變爲String類型,另外對於布爾類型可以加單引號或不加單引號,都可以正常反序列化.

(3)數組

a.普通數組

int [] a = new int []{1,2,3};
double [] b = new double []{1.0,2.0,3.0};
String [] c = new String []{"123","456"};

System.out.println(gson.toJson(a));
System.out.println(gson.toJson(b));
System.out.println(gson.toJson(c));

int [] aa = gson.fromJson("[6,7,8]",int [].class);
double [] bb = gson.fromJson("[6.0,8.0,9.0]",double [].class);
String [] cc = gson.fromJson("['123123','5464']",String [].class);

System.out.println(Arrays.toString(aa));
System.out.println(Arrays.toString(bb));
System.out.println(Arrays.toString(cc));

在這裏插入圖片描述
對普通數組的話,{}變成了[].
還有就是反序列化時,默認會在逗號後面添一個空格.

b.List

List<String> list = new ArrayList<>();
list.add("123");
list.add("456");

System.out.println(gson.toJson(list));

List<AAAA> list2 = new ArrayList<>();
list2.add(new AAAA());
list2.add(new AAAA("45345",8888,false));

System.out.println(gson.toJson(list2));
System.out.println("---------------------------------");

Type type = new TypeToken<List<String>>(){}.getType();

String strList = "['234234','45457']";
List<String> list3 = gson.fromJson(strList, type);
System.out.println(list3);

String strList2 = "[" +
    "{'field1':'9999','field2':666,'field3':'true'}" +
    "," +
    "{'field1':'sdlkfkl','field2':-234234,'field3':'false'}" +
    "]";
List<AAAA> list4 = gson.fromJson(strList2, new TypeToken<List<AAAA>>(){}.getType());
System.out.println(list4);
class AAAA 
{
    private String field1 = "123";
    private int field2 = 5;
    private boolean field3 = true;
}

在這裏插入圖片描述
序列化沒什麼問題,直接toJson即可,反序列化時,需要配合java.lang.reflect.Type使用,明確指出所要轉換的類型:

Type type = new TypeToken<List<String>>(){}.getType();

TypeToken<T>中T爲所需要的類型,再把這個Type對象傳遞給fromJson即可完成轉換.
List序列化出來的用[]表示.

(4)Map

Map<String,Integer> map = new HashMap<>();
map.put("123",88);
map.put("2349",999);

System.out.println(gson.toJson(map));

String str = "{'123':23423423,'9999':-234234}";
Map<String,Integer> map2 = gson.fromJson(str, new TypeToken<Map<String,Integer>>(){}.getType());
System.out.println(map2.get("123").toString());
System.out.println(map2.get("9999").toString());

在這裏插入圖片描述
Map同樣序列化時直接toJson,反序列化時使用java.lang.reflect.Type.Map序列化出來的用{}表示.

(5)Set

Set<String> set = new HashSet<>();
set.add("123123");
set.add("2349594");
set.add("-234()@#$@#");
System.out.println(gson.toJson(set));

String str = "['38483','@*#$(@#$)','SD<FGDF>G']";
Set<String> set2 = gson.fromJson(str, new TypeToken<Set<String>>(){}.getType());
set2.stream().forEach(System.out::println);

在這裏插入圖片描述
set序列化出來的用[]表示.

(6)null

System.out.println(gson.toJson(new AAA()));
String str = "{'field2':333,'field3':null}";
System.out.println(gson.fromJson(str, AAA.class));
class AAA
{
    private String field1 = null;
    private Integer field2 = null;
    private Double field3 = 3.0;

    @Override
    public String toString()
    {
        return "field1:"+field1+",field2:"+field2+",field3:"+field3;
    }
}

在這裏插入圖片描述
Gson會忽略空值,在序列化時看不到null對應的鍵值對,反序列化時,直接對應爲空.

(7)混合

一個對象裏面包含了List,Map,Set,null.

System.out.println(gson.toJson(new Test()));
class Test
{
    private List<String> listField = new ArrayList<>();
    private Map<String,Double> mapField = new HashMap<>();
    private Set<Integer> setField = new HashSet<>();

    private List<User> userListFiled = new ArrayList<>();
    private Map<String,User> userMapField = new HashMap<>();
    private Set<User> userSetField = new HashSet<>();

    private Set<String> nullSetField = null;
    private Map<String,User> nullMapField = null;
    private List<User> nullListField = null;

    private Long longField = 23423423423L;
    private int intField = 234234;
    private Double doubleField = 234234.23423;
    private User userField = new User();

    {
        listField.add("234234");
        mapField.put("23432",-234.0);
        setField.add(-23423);

        userListFiled.add(new User("3459",-23423,new int []{4,5,5}));
        userListFiled.add(new User());
        userMapField.put("()",new User());
        userMapField.put("------", new User("345345",3434,new int []{4,44}));
        userSetField.add(new User());
        userSetField.add(new User());
    }
}

class User
{
    private String name = "noName";
    private int age = 0;
    private int [] nums = new int[]{2,3,4};

    public User()
    {

    }

    public User(String name,int age,int [] nums)
    {
        this.name = name;
        this.age = age;
        this.nums = nums;
    }
}

在這裏插入圖片描述
這個就不反序列化了,可以看到對象,Map用{},List,Set用[].與上面的一致.

5.Gson註解

(1)@SerializedName

SerializedName註解有兩個參數,分別是value與alternate.SerializedName由註解名字可以知道與序列化成的Json名字有關.

默認情況下,json的鍵名與對象的字段名一致,@SerializedName可以解決序列化/反序列化時json鍵名與對象字段名不一致的問題,使其將json可以正確映射到對應的字段.

a.單獨使用value

單獨使用一個參數時,即@SerializedName("xxx")或@SerializedName(value="xxx"),序列化時,對象字段會變爲@SerializedName中的值,反序列化時,若不是@SerializedName()中的值則不會反序列化:

public class SerializedNameValueTest
{
    public static void main(String[] args) {
        Gson gson = new Gson();
        System.out.println(gson.toJson(new SerializedNameValueTest().new User()));

        String str = "{'this is a name':'what????','this is an age':13,'email':'[email protected]'}";
        System.out.println(gson.fromJson(str,User.class));

        String str2 = "{'name':'askldfklaslk','age':-222,'email':'[email protected]'}";
        System.out.println(gson.fromJson(str2, User.class));
    }

    class User
    {
        @SerializedName("this is a name")
        private String name = "123";

        @SerializedName("this is an age")
        private int age = 0;

        private String email = "[email protected]";

        private double[] nums = new double[] { 1.0, 2.0 };

        @Override
        public String toString() {
            return "name:" + name + ",age:" + age + ",eamil:" + email + ",nums:" + Arrays.toString(nums);
        }
    }
}

在這裏插入圖片描述
最後一個反序列化不成功,因爲'name'不對應@SerializedName()中的'this is a name'.

b.同時使用value與alternate

同時使用兩者可以解決上面的問題,即name字段--->json中的'this is a name'--->name字段:

public class SerializedNameValueAndAlternateTest
{
    public static void main(String[] args) {
        Gson gson = new Gson();
        System.out.println(gson.toJson(new SerializedNameValueAndAlternateTest().new User()));

        String str = "{'this is a name':'what????','this is an age':13,'email':'[email protected]'}";
        System.out.println(gson.fromJson(str, User.class));

        String str2 = "{'name':'askldfklaslk','age':-222,'email':'[email protected]'}";
        System.out.println(gson.fromJson(str2, User.class));
    }

    class User {
        @SerializedName(value = "this is a name",alternate = "name")
        private String name = "123";

        @SerializedName(value = "this is an age",alternate = "age")
        private int age = 0;

        private String email = "[email protected]";

        private double[] nums = new double[] { 1.0, 2.0 };

        @Override
        public String toString() {
            return "name:" + name + ",age:" + age + ",eamil:" + email + ",nums:" + Arrays.toString(nums);
        }
    }
}

alternate就是爲反序列化準備的,若找不到value中對應的值,則尋找alternate對應的值,找不到再設爲null.

在這裏插入圖片描述

若同時存在value與alternate:

String str3 = "{'name':'altername','this is a name':'value'}";
System.out.println(gson.fromJson(str3, User.class));
String str4 = "{'this is a name':'value','name':'altername'}";
System.out.println(gson.fromJson(str4, User.class));

則以"最晚"出現的值爲標準.

在這裏插入圖片描述

(2)@Expose

@Expose可以忽略某個字段,有兩個參數:

  • serialize
  • deserialize

默認情況下都是true,分別表示序列化與反序列化.

System.out.println(gson.toJson(new User()));
class User
{
    @Expose(serialize = false)
    private String name = "123";

    @Expose(deserialize = false)
    private int age = 0;

    @Expose(serialize = false,deserialize = false)
    private String email = "[email protected]";

    @Expose(serialize = true,deserialize = true)
    private double [] nums = new double [] {1.0,2.0};
}

name不允許序列化,age不允許反序列化,email不允許序列化與反序列化,nums既允許也允許反序列化.(此時效果等於沒加@Expose)
輸出:
在這裏插入圖片描述
咦?好像沒用的樣子?
因爲Gson對象會默認忽略@Expose,想要@Expose生效需要使用Gson Builder:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.excludeFieldsWithoutExposeAnnotation();
Gson gson = gsonBuilder.create();
System.out.println(gson.toJson(new User()));

String str = "{'name':'234','age':-3,'email':'23423','nums':[3,4,3]}";
System.out.println(gson.fromJson(str, User.class));
class User
{
    @Expose(serialize = false)
    private String name = "123";

    @Expose(deserialize = false)
    private int age = 0;

    @Expose(serialize = false,deserialize = false)
    private String email = "[email protected]";

    @Expose(serialize = true,deserialize = true)
    private double [] nums = new double [] {1.0,2.0};

    @Override
    public String toString()
    {
        return "name:"+name+",age:"+age+",eamil:"+email+",nums:"+Arrays.toString(nums);
    }
}

在這裏插入圖片描述
不能反序列化age與email,輸出的是age與email的默認值.

6.Gson Builder

前面的例子基本上都是通過

Gson gson = new Gson();

來直接實例化一個Gson來使用Gson的,使用Gson Builder可以設置Gson的某些屬性,使用其中的create()返回一個Gson.

(1)基礎

創建一個簡單的Gson,通過create()創建:

GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();

(2)命名規則

a.使用系統命名規則

使用

gsonBuilder.setFieldNamingPolicy();

設置json中鍵的命名規則.6個值可選:
在這裏插入圖片描述

  • IDENTITY:相同,json中的鍵名與字段名相同.
  • LOWER_CASE_WITH_DASHES:在原來字段的大寫字母前加-,並且把大寫變成小寫
  • LOWER_CASE_WITH_DOTS:在原來字段的大寫字母前加.,並且把大寫變成小寫
  • LOWER_CASE_WITHUNDERSCORES:在原來字段的大寫字母前加,並且把大寫變成小寫
  • UPPER_CAMEL_CASE:首字母大寫
  • UPPER_CAMEL_CASE_WITH_SPACES:在原來字段的大寫字母前加空格
public class NamingRulesTest
{
    public static void main(String[] args) {
        NamingRulesTest n = new NamingRulesTest();
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY);
        System.out.println(gsonBuilder.create().toJson(n.new User()));

        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);
        System.out.println(gsonBuilder.create().toJson(n.new User()));

        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DOTS);
        System.out.println(gsonBuilder.create().toJson(n.new User()));

        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        System.out.println(gsonBuilder.create().toJson(n.new User()));

        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
        System.out.println(gsonBuilder.create().toJson(n.new User()));

        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES);
        System.out.println(gsonBuilder.create().toJson(n.new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private String Name_Name_name = "123";
        @SuppressWarnings("unused")
        private int _age_age_Age_age = 645;
        @SuppressWarnings("unused")
        private double numsNumsNums = 34.45;
    }
}

在這裏插入圖片描述
注意,若某個字段有了@SerializedName,則這個字段遵循@SerializedName的策略.

b.自定義命名規則

重寫FieldNamingStrategy中的translateName(Field field),把自定義的FieldNamingStrategy傳遞給GsonBuilder的setFieldNamingStrategy().
比如想要前綴加上某人的名字:

public class CustomNamingRulesTest
{
    public static void main(String[] args) {
        FieldNamingStrategy myNamingStrategy = new FieldNamingStrategy(){
            @Override
            public String translateName(Field field) {
                return "kr"+field.getName();
            }
        };

        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingStrategy(myNamingStrategy);
        Gson gson = gsonBuilder.create();
        System.out.println(gson.toJson(new CustomNamingRulesTest().new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private String name = "123";
        @SuppressWarnings("unused")
        private int num = 5;
    }
}

在這裏插入圖片描述

(3)null

默認情況下,Gson實例不允許序列化null,如果想要序列化null,藉助GsonBuilder的serializeNulls()方法:

public class NullTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.serializeNulls();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES);
        System.out.println(gsonBuilder.create().toJson(new NullTest().new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private String thisIsANullField;
        @SuppressWarnings("unused")
        private Integer andThisIsANullFieldToo;
    }
}

在這裏插入圖片描述

(4)排除

前面已經接觸到了@Expose這樣的排除策略,下面看看更加強大的配合GsonBuilder使用的排除策略.主要有四種:屬性名排除,類型排除,修飾符排除,@Expose排除.

a.屬性名排除

public class ExclusionNameTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.serializeNulls();
        ExclusionStrategy exclusionName = new ExclusionStrategy(){
            @Override
            public boolean shouldSkipField(FieldAttributes f)
            {
                return f.getName().endsWith("e");
            }

            @Override
            public boolean shouldSkipClass(Class<?> arg0) {
                return false;
            }
        };
        gsonBuilder.setExclusionStrategies(exclusionName);

        System.out.println(gsonBuilder.create().toJson(new ExclusionNameTest().new User()));
    }

    class User
    {
        private String name;
        private int num;
    }
}

排除字段名以e結尾的字段.下面是重點:

public boolean shouldSkipField(FieldAttributes f)
{
    return f.getName().endsWith("e");
}

重寫的shouldSkipField從名字可以看出跳過某些字段,返回true表示跳過,即排除這個字段.上面的例子中若名字以e結尾則跳過,因此輸出:
在這裏插入圖片描述

b.類型排除

public class ExclusionTypeTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.serializeNulls();
        ExclusionStrategy exclusionType = new ExclusionStrategy(){

            @Override
            public boolean shouldSkipField(FieldAttributes arg0) {
                return false;
            }

            @Override
            public boolean shouldSkipClass(Class<?> cls) {
                return cls == String.class;
            }
        };
        gsonBuilder.setExclusionStrategies(exclusionType);

        System.out.println(gsonBuilder.create().toJson(new ExclusionNameTest().new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private String name;
        @SuppressWarnings("unused")
        private int num;
    }
}

重寫的shouldSkipClass表示要跳過的類,這裏跳過了String,只剩下num.

@Override
public boolean shouldSkipClass(Class<?> cls) {
    return cls == String.class;
}

在這裏插入圖片描述

c.修飾符排除

這個不用重寫方法了,直接使用GsonBuilder的excludeFieldsWithModifiers(),參數是java.lang.reflect.Modifier:
在這裏插入圖片描述
這個可選的比較多就不一一列舉了,只選了一個final的例子:

public class ExclusionModifierTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.excludeFieldsWithModifiers(Modifier.FINAL);
        System.out.println(gsonBuilder.create().toJson(new User()));
    }
}

class User
{
    @SuppressWarnings("unused")
    private final String name = "123";
    @SuppressWarnings("unused")
    private static int num;
}

在這裏插入圖片描述

d.@Expose排除

這個準確來說是排除沒有被@Expose註解的字段:

public class ExclusionExposeTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.excludeFieldsWithoutExposeAnnotation();
        System.out.println(gsonBuilder.create().toJson(new ExclusionExposeTest().new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private String name;
        @SuppressWarnings("unused")
        private int num;
        @Expose(serialize = true,deserialize = true)
        private int age;
    }
}

在這裏插入圖片描述

(5)Lenient

這是有關於反序列化時JSON的容錯機制的問題,因爲通常來說將一個對象通過Gson轉換成json沒什麼大問題,但是將json轉換爲對象的時候就...就難說了,因爲不知道是否符合標準的json格式,因此Gson提供了一定的容錯機制,就是Lenient.

Lenient翻譯過來是"寬容的"的意思,可以通過:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLenient();

開啓.
Gson內部有一個JsonReader類,默認情況下,JsonReader是嚴格的且僅接受符合RFC 4627標準的json(RFC 4627標準可以看看這裏),設置爲lenient後可以"容忍"以下幾種錯誤:

  • a.以 ) ] } ' \n 開頭
  • b.多個頂層值.
  • c.任何類型的頂層值
  • d.數字可能是NaNs或infinites
  • e.以行註釋//或#結尾
  • f.以一個換行符結束
  • g.C風格的註釋/**/,可能會嵌套
  • h.鍵/字符串沒有引號或者單引號
  • i.數組元素以;分隔
  • j.不必要的數組分隔符,"默認"null爲省略值,比如[1,,2],"默認"第二個元素爲null
  • k.鍵值以=或=>分隔而非使用:
  • l.鍵值對以;分隔而非使用,

設置setLenient()後,Gson會儘可能解析有錯誤的json,若實在無能爲力,會拋出MalformedJsonException異常.
下面是一個不嚴格的json的例子:

public class LenientTest
{
    public static void main(String[] args)
    {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setLenient();

        String str = "{'name'='234';'num'=6}";
        System.out.println(gsonBuilder.create().fromJson(str,User.class));
    }

    class User
    {
        private String name;
        private int num;

        @Override
        public String toString()
        {
            return "name:"+name+",num:"+num;
        }
    }
}

在這裏插入圖片描述
下面是一個異常的例子:

String errorStr = "{'name'=????,,,,,,}";
System.out.println(gsonBuilder.create().fromJson(errorStr,User.class));

在這裏插入圖片描述

(6)Floats & Doubles

先看一個例子:

public class FloatTest
{
    public static void main(String[] args) {
        Gson gson = new Gson();
        System.out.println(gson.toJson(new FloatTest().new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private float f = Float.POSITIVE_INFINITY;
    }
}

會報錯:
在這裏插入圖片描述
它說Infinity在JSON標準是一個非法值.想要重寫這個行爲需要使用GsonBuilder.serializeSpecialFloatingPointValues().
因爲JSON規範不允許NaN,-Infinity,Infinity,因此會報錯.下面使用GsonBuilder:

public class FloatTest
{
    public static void main(String[] args) {
        // Gson gson = new Gson();
        // System.out.println(gson.toJson(new FloatTest().new User()));

        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.serializeSpecialFloatingPointValues();
        System.out.println(gsonBuilder.create().toJson(new FloatTest().new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private float f = Float.POSITIVE_INFINITY;
    }
}

直接輸出Infinity:
在這裏插入圖片描述

double也類似:

public class DoubleTest
{
    public static void main(String[] args)
    {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.serializeSpecialFloatingPointValues();
        System.out.println(gsonBuilder.create().toJson(new DoubleTest().new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private double ni = Double.NEGATIVE_INFINITY;
        @SuppressWarnings("unused")
        private double pi = Double.POSITIVE_INFINITY;
        @SuppressWarnings("unused")
        private double nan = Double.NaN;
    }
}

在這裏插入圖片描述

(7)模型版本化

通過@Since與@Until添加版本控制,控制某個版本在序列化與反序列化時忽略或忽略某個字段.
@Since表示從某個版本開始這個字段不忽略,@Until表示這個版本後將忽略該字段.

需要配合GsonBuilder的setVersion使用,設定版本號.

public class VersionTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.serializeNulls();
        System.out.println(gsonBuilder.setVersion(0.9).create().toJson(new VersionTest().new User()));
        System.out.println(gsonBuilder.setVersion(1.0).create().toJson(new VersionTest().new User()));
        System.out.println(gsonBuilder.setVersion(1.4).create().toJson(new VersionTest().new User()));
        System.out.println(gsonBuilder.setVersion(1.5).create().toJson(new VersionTest().new User()));
    }

    class User
    {
        @Since(1.0)
        private String name;
        @Until(1.4)
        private int num;
    }
}

在這裏插入圖片描述
@Since的範圍包含了左區間端點,@Until的範圍不包含右區間端點.

(8)格式化日期

可以使用setDateForamt()來格式化日期輸出:

gsonBuilder.setDateFormat(int style);
gsonBuilder.setDateFormat(String pattern);
gsonBuilder.setDateFormat(int dateStyle,int timeStyle);

第一個函數的參數爲DateFormat中的常量值,第二個函數的參數是類似SimpleDateFormat中的String,第三個函數的參數與第一個類似,分開日期與時間設置.
再說以下setPrettyPrinting()這個函數,看名字就知道,美化打印的.會加上空格.

public class FormatTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();

        System.out.println(gsonBuilder.setDateFormat(DateFormat.SHORT, DateFormat.SHORT).create().toJson(new FormatTest().new User()));
        System.out.println(gsonBuilder.setDateFormat(DateFormat.SHORT,DateFormat.LONG).create().toJson(new FormatTest().new User()));
        System.out.println(gsonBuilder.setDateFormat(DateFormat.MEDIUM, DateFormat.MEDIUM).create().toJson(new FormatTest().new User()));
        System.out.println(gsonBuilder.setDateFormat(DateFormat.LONG, DateFormat.MEDIUM).create().toJson(new FormatTest().new User()));
        System.out.println(gsonBuilder.setDateFormat(DateFormat.FULL, DateFormat.FULL).create().toJson(new FormatTest().new User()));
        System.out.println(gsonBuilder.setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(new FormatTest().new User()));
    }

    class User
    {
        @SuppressWarnings("unused")
        private Date date = new Date();
    }
}

在這裏插入圖片描述

7.Gson進階

(1)枚舉

枚舉其實也與普通的字段類似,序列化與反序列化正常操作.

public class EnumTest
{
    public static void main(String[] args) {
        Gson gson = new Gson();
        System.out.println(gson.toJson(new EnumTest().new User()));

        String str = "{\"season\":\"SUMMER\",\"name\":\"8888\"}";
        System.out.println(gson.fromJson(str,User.class));
    }

    enum Season
    {
        SPRING,
        SUMMER,
        AUTUMN,
        WINTER
    }

    class User
    {
        private Season season = Season.SPRING;
        private String name = "123";

        @Override
        public String toString()
        {
            return "season:"+season.toString()+",name:"+name;
        }
    }
}

在這裏插入圖片描述
主要配合@SerializedName()使用,例如對於月份,可以使用1,2,3來表示:

public class EnumTest
{
    public static void main(String[] args) {
        Gson gson = new Gson();
        System.out.println(gson.toJson(new EnumTest().new User()));

        String str = "{\"season\":\"SUMMER\",\"name\":\"8888\",\"month\":\"2\"}";
        System.out.println(gson.fromJson(str,User.class));
    }

    enum Season
    {
        SPRING,
        SUMMER,
        AUTUMN,
        WINTER
    }

    class User
    {
        private Season season = Season.SPRING;
        private String name = "123";
        private Month month = Month.January;

        @Override
        public String toString()
        {
            return "season:"+season.toString()+",name:"+name+",month:"+month.toString();
        }
    }

    enum Month
    {
        @SerializedName("1")
        January,
        @SerializedName("2")
        February,
        @SerializedName("3")
        March,
        @SerializedName("4")
        April,
        @SerializedName("5")
        May,
        @SerializedName("6")
        June,
        @SerializedName("7")
        July,
        @SerializedName("8")
        August,
        @SerializedName("9")
        September,
        @SerializedName("10")
        October,
        @SerializedName("11")
        November,
        @SerializedName("12")
        December
    }
}

在這裏插入圖片描述

(2)泛型

這裏主要說一下反序列化泛型.因爲序列化泛型...直接toJson()就好了.
想想這樣的情景:有一個待解析的Json String,類型爲List<T>,因此想有一個這樣的方法:

public <T> List<T> fromJSON(String json,Class<T> cls);

好了,怎麼寫呢,之前用的是TypeToken()實現反序列化:

List<Integer> integerList = new ArrayList<>();
integerList.add(3);
integerList.add(-999);
gson.toJson(integerList, new TypeToken<List<Integer>>() {}.getType());

答案就是使用ParameterizedType:重寫裏面的getActualTypeArguments(),getOwnerTpye()與getRawType():

public class GenericsTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();
        Gson gson = gsonBuilder.create();

        List<Integer> integerList = new ArrayList<>();
        integerList.add(3);
        integerList.add(-999);
        gson.toJson(integerList, new TypeToken<List<Integer>>() {}.getType());

        List<String> stringList = new ArrayList<>();
        stringList.add("12321");
        stringList.add("(@)#$@#");
        String t1 = gson.toJson(integerList);
        String t2 = gson.toJson(stringList);

        new GenericsTest().fromJSON(t1, Integer.class).stream().forEach(System.out::println);
        new GenericsTest().fromJSON(t2, String.class).stream().forEach(System.out::println);
    }

    public <T> List<T> fromJSON(String json,Class<T> cls)
    {
        return new Gson().fromJson(json,new ParameterizedTypeImpl(cls));
    }

    class ParameterizedTypeImpl implements ParameterizedType
    {
        private Class cls;
        public ParameterizedTypeImpl(Class cls)
        {
            this.cls = cls;
        }

        @Override
        public Type[] getActualTypeArguments()
        {
            return new Type[]{cls};
        }

        @Override
        public Type getRawType()
        {
            return List.class;
        }

        @Override
        public Type getOwnerType()
        {
            return null;
        }
    }
}

在這裏插入圖片描述

@Override
public Type[] getActualTypeArguments()
{
    return new Type[]{cls};
}

返回實際參數類型數組,在這裏是String.class與Integer.class.

@Override
public Type getRawType()
{
    return List.class;
}

返回聲明這個類型的類或接口,在這裏是List.class.

@Override
public Type getOwnerType()
{
    return null;
}

返回其成員之一的類型,就是說如果完全限定類名爲A.B,則返回A,在這裏沒有A,因此爲null.
定義好實現ParameterizedTpye接口的類後,把它傳遞給fromJson()作爲第二參數,構造方法的參數爲List<T>中的T.class.

(4)自定義序列化

自定義序列化一般用於自定義簡化json.
比如有一個User類:

class User
{
    private String name = "123";
    private String email = "[email protected]";
    private int [] nums = new int [] {1,2,3};
}

想要不序列化name,可以使用@Expose(serialize = false),但是想要部分序列化nums,比如只是需要第一個nums[0],上面的@Expose,@SerializedName等註解都用不上,這時需要使用JsonSerializer自定義序列化,重寫其中的:

public JsonElement serialize(T t,Type type,JsonSerializationContext context)

可以返回一個JsonObject,這裏的JsonObject可以自定義添加屬性(即鍵值對).
需要配合GsonBuilder使用,創建了自己的JsonSerializer<T>後,把它傳遞給GsonBuilder的registerTypeAdapter():

public class SerializeCustomTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        JsonSerializer<User> serializer = new JsonSerializer<SerializeCustomTest.User>() {
            @Override
            public JsonElement serialize(User u,Type type,JsonSerializationContext context)
            {
                JsonObject object = new JsonObject();
                object.addProperty("name",u.getName());
                object.addProperty("email",u.getEmail());
                object.addProperty("nums",u.getNums()[0]);
                return object;
            }
        };
        gsonBuilder.registerTypeAdapter(User.class,serializer);
        System.out.println(gsonBuilder.create().toJson(new SerializeCustomTest().new User()));
    }

    class User
    {
        private String name = "123";
        private String email = "[email protected]";
        private int [] nums = new int [] {1,2,3};

        public int [] getNums()
        {
            return nums;
        }

        public String getName()
        {
            return name;
        }

        public String getEmail()
        {
            return email;
        }
    }
}

在這裏插入圖片描述

(5)自定義反序列化

自定義反序列化主要就是針對有多餘鍵值對的json,比如User只需要name字段,但是傳來的json包含了諸如age這樣的鍵,因此只需挑選所需的進行反序列化.與序列化類似,首先定義自己的JsonDeserializer<T>,重寫其中的:

public T deserialize(JsonElement,Type,JsonDeserializationContext);

返回一個自定義的反序列化的對象.最後在GsonBuilder中registerTypeAdapter()即可.

public class DeserializedCustomTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        JsonDeserializer<User> deserializer = new JsonDeserializer<DeserializedCustomTest.User>() {
            @Override
            public User deserialize(JsonElement json,Type type,JsonDeserializationContext context)
            {
                JsonObject object = json.getAsJsonObject();
                User user = new DeserializedCustomTest().new User(object.get("name").getAsString());
                return user;
            }
        };
        gsonBuilder.registerTypeAdapter(User.class, deserializer);

        String str = "{\"aaa\":\"bbbb\",\"name\":\"this is a name\",\"age\":\"444\"}";
        System.out.println(gsonBuilder.create().fromJson(str,User.class));
    }

    class User
    {
        private String name;
        private int age;

        public User(String name)
        {
            this.name = name;
        }

        @Override
        public String toString()
        {
            return "name:"+name+",age:"+age;
        }
    }
}

在這裏插入圖片描述

(6)默認實例

可以設置反序列化時的默認值,通過構造方法實現.比如json中沒有所需要的字段的值,默認情況下爲null,如果想要不爲null,可以設定默認值,對於對象可以設定構造方法.

通過實現InstanceCreator<T>來實現,重寫其中的createInstance方法,再配合GsonBuilder的registerTypeAdapter().

public class InstanceCustomTest
{
    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(
            AA.class,
            new InstanceCustomTest().new AAInstance(
                new InstanceCustomTest().new BB("123","456")
            )
        );

        String str = "{\"name\":\"123123\"}";
        System.out.println(gsonBuilder.create().fromJson(str, AA.class));
    }

    class AAInstance implements InstanceCreator<AA>
    {
        private BB bb;
        public AAInstance(BB bb)
        {
            this.bb = bb;
        }

        @Override
        public AA createInstance(Type type)
        {
            return new AA(bb);
        }
    }

    class AA
    {
        private String name = "AA";
        private BB bb;

        public AA(BB bb)
        {
            this.bb = bb;
        }

        @Override
        public String toString()
        {
            return "name:"+name+",bb:"+bb.toString();
        }
    }

    class BB
    {
        private String field1;
        private String field2;

        public BB(String field1,String field2)
        {
            this.field1 = field1;
            this.field2 = field2;
        }

        @Override
        public String toString()
        {
            return "field1:"+field1+",field2:"+field2;
        }
    }
}

json沒有爲BB類設定值,採用默認值.

(7)@JsonAdapter

這個是自定義序列化/反序列化的註解,可以簡化JsonSerializer與JsonDeserializer.另外不用配合GsonBuilder使用,直接使用new Gson().toJson()/fromJson()即可,比直接使用JsonSerializer與JsonDeserializer要簡單.
首先使一個類實現JsonSerializer<T>接口,接着把這個類作爲要註解的字段的@JsonAdapter的參數:

class Serializer implements JsonSerializer<User>
class ContainUser
{
    private String userId = "x";
    @JsonAdapter(Serializer.class)
    private User user = new User();
}
public class JsonAdapterSerializeTest
{
    public static void main(String[] args)
    {
        Gson gson = new Gson();
        System.out.println(gson.toJson(new JsonAdapterSerializeTest().new ContainUser()));
    }

    class Serializer implements JsonSerializer<User>
    {
        @Override
        public JsonElement serialize(User u,Type type,JsonSerializationContext context)
        {
            JsonObject object = new JsonObject();
            object.addProperty("name",u.getName());
            object.addProperty("email",u.getEmail());
            object.addProperty("nums",u.getNums()[0]);
            return object;
        }
    }

    class ContainUser
    {
        private String userId = "x";
        @JsonAdapter(Serializer.class)
        private User user = new User();
    }

    class User
    {
        private String name = "123";
        private String email = "[email protected]";
        private int [] nums = new int [] {1,2,3};

        public int [] getNums()
        {
            return nums;
        }

        public String getName()
        {
            return name;
        }

        public String getEmail()
        {
            return email;
        }
    }
}

在這裏插入圖片描述
反序列化也類似.

public class JsonAdapterDeserializeTest
{
    public static void main(String[] args) {
        Gson gson = new Gson();
        String str = "{\"aaaa\":\"bbbb\",\"name\":\"this is a name\",\"age\":\"444\"}";
        System.out.println(gson.fromJson(str,User.class));
    }

    class Deserialize implements JsonDeserializer<User>
    {
        @Override
        public User deserialize(JsonElement json,Type type,JsonDeserializationContext context)
        {
            JsonObject object = json.getAsJsonObject();
            User user = new JsonAdapterDeserializeTest().new User(object.get("name").getAsString());
            return user;
        }
    }

    @JsonAdapter(Deserialize.class)
    class User
    {
        private String name = "123";
        public User()
        {

        }
        public User(String name)
        {
            this.name = name;
        }

        @Override
        public String toString()
        {
            return "name:"+name;
        }
    }
}

在這裏插入圖片描述
實現JsonDeserializer<T>,重寫deserialize(),在所需的類上添加註解.

注意@JsonAdapter註解只能添加在類上.

8.源碼

所有例子的完整源碼.
github.
碼雲.
Gson官方github.

9.參考鏈接

1.Gson Tutorial Series by Future Studio

2.gson完全教程

3.gson教程

4.gson用戶指南

5.Gson User guide

6.gson

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