1. 程式人生 > >關於Gson的一些使用方法總結

關於Gson的一些使用方法總結

1. 基本使用方法[fromJson/toJson]

將字串解析成相應的物件 or 將物件Json化 這個應該是最常用的吧

Gson gson = new Gson();
String result = gson.fromJson("str",String.class);
int result1 = gson.fromJson("100",int.class);
Double result2 = gson.fromJson("210.34",Double.class);

Person person = new Person();
result = gson.toJson(person)

2.屬性名不一致情況 使用@SerializedName

這個問題主要是三端(server/前端/客戶端)命名習慣不一致造成的,舉個例子:

// 客戶端 java:
private String userName = "zhangsan";

//前端 javascript:
let user_name = "zhangsan"

//後端 PHP:
$username = "zhangsan";

那麼相應的json為:

"{'userName':'zhangsan'}"
"{'user_name':'zhangsan'}"
"{'username':'zhangsan'}"

最為客戶端,需要相容這些模式時,需要使用@SerializedName:

    @SerializedName(value = "userName", alternate = {"user_name","username"})
    public String userName;

其中value是當前預設解析欄位,沒有的話,查詢有沒有user_name或者username,有的話,將對應的值對映到該欄位上。相當於alernate關鍵字是備選欄位。

3. 使用GsonBuilder輸出null

我們定義一個物件Student:

public class Student {

    private String name ;
    private
int age ; //getter.setter方法 }

預設情況下,null欄位是不輸出的:

Student s = new Student();
String str = gson.toJson(s);

System.out.println(str)

結果為:

{"age":0}

如果需要輸入name,那麼需要定義GsonBuilder:

 //自定義GsonBuilder
  Gson gson = new GsonBuilder().serializeNulls().create();
  gson.toJson(s, System.out);

輸出為:

{"name":null,"age":0}

4.輸出格式化Gson

Gson gson2 = new GsonBuilder().serializeNulls().setPrettyPrinting().create();

輸出:

{
  "name": null,
  "age": 0
}

5.設定時間格式:

 Gson gson2 = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
 gson2.toJson(new java.util.Date(), System.out);

輸出為:

"2018-07-09"

6.生成不可以用的Json字串[貌似沒什麼卵用]

 Gson gson2 = new GsonBuilder().setDateFormat("yyyy-MM-dd").generateNonExecutableJson().create();
 gson2.toJson(new java.util.Date(), System.out);

輸出為:

)]}'
"2018-07-09"

7.禁止Html字串轉義:

String html  = "<p> hello world</p>" ;

Gson gson2 = new GsonBuilder().disableHtmlEscaping().create();
gson2.toJson(html,System.out);

禁止字元轉義之後,變為:

"<p> hello world</p>"

未禁止轉義之後:

"\u003cp\u003e hello world\u003c/p\u003e"  //UTF-8轉義

8.欄位過濾

主要的原因是安全因素,人為新增欄位,有時候序列化&反序列化需要過濾這些欄位:

8.1 @Expose欄位:

Expose中文名稱是曝光的意思,就是你添加了,它就會序列化/反序列化了,沒有的話就算了。

Student類中新增”address”欄位:

public class Student {

    private String name ;
    private int age ;

    @Expose(serialize = true, deserialize = true)
    private String address;

}

那麼toGson為:

Student student = new Student("tom",12,"sh");
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
gson.toJson(student,System.out);

輸出為:

{"address":"sh"}

當然@Expost註解上還有serializedeserialize關鍵字,按字面意思在序列化和反序列化時,要不要將該欄位接入操作。

8.2 基於版本,基於註解@Until和@Since

@Until是 <= 某Version

@Since是 >= 某Version

 @Since(18)
 private int age ;

設定當前版本:

Student student = new Student("tom",12,"sh");
Gson gson = new GsonBuilder().setVersion(17).create();
gson.toJson(student,System.out);

當前設定版本17 < @Since(18)此時,此時將不會輸出age:

{"name":"tom","address":"sh"}

更改Version:

Gson gson = new GsonBuilder().setVersion(20).create();

當前設定版本20 > @Since(18)此時,此時輸出為:

{"name":"tom","age":12,"address":"sh"}

同理,@Until也是類似的,將不再做類比了。

當然對於同一個欄位,你可以同時使用@Since和@Until

@Since(20) 
@Until(5)
private String address;

8.3 基於訪問修飾符

設定相應的修飾符物件欄位:

public class ModifierTest {
    private String privateName = "privateName";
    public String publicName = "publicName";
    public final String finalName = "finalName";
    public final static String finalStaticName = "finalStaticName";
}

使用Modifier欄位過濾:

ModifierTest test = new ModifierTest();

//去除private修飾符欄位
Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE).create();

gson.toJson(test,System.out);

輸出為:

{"publicName":"publicName","finalName":"finalName","finalStaticName":"finalStaticName"}

去掉final 和 public欄位:

Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.FINAL, Modifier.PUBLIC).create();

輸出為:

{"privateName":"privateName"}

8.4 自定義規則

直接使用Gson中提供的ExclusionStrategy介面:

Student student = new Student("tom",12,"sh");

Gson gson = new GsonBuilder().addSerializationExclusionStrategy(new ExclusionStrategy() {

    //跳過的field將不參與序列化/反序列化
    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        //name 直接跳過
        if("name".equals(f.getName())) return true;

        Expose expose = f.getAnnotation(Expose.class);
        if(expose != null && !expose.deserialize()) return true ; //按照Expose註解 跳過
            return false;
        }

        @Override
        public boolean shouldSkipClass(Class<?> clazz) {
            //直接排除某些特定的類
            return clazz == int.class || clazz == Integer.class;
        }
    }).create();

    gson.toJson(student, System.out);

輸出為:

{"address":"sh"}

9. 自定義TypeAdapter

很多時候,我們都會遇到這種情況,我們定義age欄位是int型的,但是伺服器給我們返回的是""欄位,如果不做處理,使用Gson解析就會丟擲NumberFormatException,所以為了解決這個容錯性,你不可能把後端拉出來打一頓,所以只有另想它法。
幸好Gson提供了TypeAdapter抽象類,可以抽象解析我們需要的資料,來個簡單一點的,直接解析註冊Int.class解析器:

String personUrl = "{\"name\" : \'zhangsan\' , \'age\' : '' , \'address\' : \'sh\'}" ;

Gson gson =  new GsonBuilder().registerTypeAdapter(int.class, new TypeAdapter<Integer>() {

@Override
public void write(JsonWriter out, Integer value) throws IOException {
        out.value(String.valueOf(value));
}

@Override
public Integer read(JsonReader in) throws IOException {
        String value = in.nextString();
        //System.out.println("------>>" + value);

        try {
            return Integer.parseInt(value);
        }catch (NumberFormatException ignore){
                    return -1;
            }
        }
        }).create() ;

       Person person = gson.fromJson(personUrl, Person.class);
       System.out.println(person);

解析之後,此時的空字串被解析成了-1:

Person{name='zhangsan', age=-1, address='sh'}

如果需要解析Student呢:
同樣一個道理:

public class StudentTypeAdapter extends TypeAdapter<Student> {

    @Override
    public void write(JsonWriter out, User value) throws IOException {
        out.beginObject();
        out.name("name").value(out.name);
        out.name("age").value(out.age);
        out.name("address").value(out.address);
        out.endObject();
    }


    @Override
    public Student read(JsonReader in) throws IOException {
        Student student = new Student();
        in.beginObject();
        while(in.hasNext()) {
            switch(in.nextName()) {
                case "name":
                    student.name = in.nextString();
                    break;

                case "age" :
                    student.age = int.nextInt();
                    break;

                case "address":
                    student.address = int.nextString();
                    break ;
            }
        }

        in.endObject();
    }
}

10. Gson與混淆

當代碼需要混淆時,有gson參與時,需要注意以下幾點:
1. 需要序列化的類,不能被混淆;

2. 使用泛型參與解析的基類BaseEntity需要實現java.io.Serializable介面。