Gson使用總結

C_YQ發表於2019-05-06

一、基礎使用

1. 常用方法

Gson.toJson(Object);
Gson.fromJson(Reader,Class);
Gson.fromJson(String,Class);
Gson.fromJson(Reader,Type);
Gson.fromJson(String,Type);
複製程式碼

2.基本資料型別解析

Gson可以結合泛型使用,但用到泛型時,需指定type,如new TypeToken<List<String>>(){}.getType(); TypeToken的構造方法是protected的,所有這麼寫new TypeToken<List<String>>(){}

Gson gson = new Gson();

反序列化:

int i = gson.fromJson("1",Integer.class);
boolean b = gson.fromJson("\"true\"",Boolean.class);
double d = gson.fromJson("1.1",Double.class);
double d1 = gson.fromJson("\"1.2\"",Double.class);
String str = gson.fromJson("TomCruise",String.class);
List<String>stringList = gson.fromJson("[\"aa\",\"bb\"]",new TypeToken<List<String>>(){}.getType());
複製程式碼

序列化:

String jsonInt = gson.toJson(2);
String jsonBoolean = gson.toJson(false);    // false
String jsonString = gson.toJson("String"); //"String"
List<String>list = new ArrayList<>();
list.add("a");
list.add("b");
String jsonList = gson.toJson(list);
複製程式碼

3.POJO類解析

POJO類:User

@JsonAdapter(UserTypeAdapter.class)
public final class User {
    @Expose @Since(4)
    private String name;
    @Expose @Until(4)
    private int age;
    @Expose private Date date;
    @Expose
    private List<User>children;
    private User parent;
//    @SerializedName(value = "email_address",alternate = {"emailAddress","email"})
    private String emailAddress;
    
    public User(String name, int age){
        this.name = name;
        this.age = age;
    }
    ...
    ...
}
複製程式碼

Gson自動化:

Gson gson = new Gson();

String json = "{\"name\":\"John\",\"age\":22,\"email_address\":\"xxx@163.com\"}";
User userF = gson.fromJson(json,User.class);//反序列化
User userT = new User("Tom",10);
userT.setAddress("xxx");
String jsonUser = gson.toJson(userT);//序列化
複製程式碼

注:字串中應注意使用轉義字元,如\"name\"

欄位重新命名:@SerializedName

Java中一般採用駝峰命名,如emailAddress,但後臺有時命名方式不同,如php形式的email_address,此時@SerializedName註解就能起大作用。

@SerializedName可以更改欄位名,還能提供備用名屬性:alternate,使用如下:

@SerializedName(value = "email_address",alternate = {"emailAddress","email"})
複製程式碼

當上面的三個屬性(email_address、email、emailAddress)中出現任意一個時都能進行解析。

:當多種情況同時出時,以最後一個出現的值為準。

4.泛型使用

Gson可以結合泛型使用,但用到泛型時,需指定type,如new TypeToken<List<String>>(){}.getType(); TypeToken的構造方法是protected的,所有這麼寫new TypeToken<List<String>>(){}; 無論使用List<User>還是List<String>,最終都是List.class泛型擦除

引入泛型可以很大程度上方便介面的設計,如後臺的返回資料格式常見兩種:

{"code":"0","message":"success","data":{}}{"code":"0","message":"success","data":[]

data的型別一種是物件,一種是資料,若無泛型的支援,則需定義兩個httpResponse類,引入泛型,則只需定義一個泛型類,不用去寫無謂的程式碼,能更專注於邏輯功能。

public final class HttpResult<T> {
    private int code;
    private boolean success;
    private T data;
    
    ...
    ...
}
複製程式碼

使用時:

Gson gson = new Gson();
Type objType = new TypeToken<HttpResult<User>>(){}.getType();
Type listType = new TypeToken<HttpResult<List<User>>>(){}.getType();
        
HttpResult<User> objUser = gson.fromJson("{\"code\":200,\"data\":{\"age\":70,\"name\":\"Cap\"},\"success\":true}",objType);
HttpResult<List<User>> listUser = gson.fromJson("{\"code\":200,\"data\":[{\"age\":70,\"name\":\"Cap\"}],\"success\":true}",listType);

gson.toJson(objUser);
gson.toJson(listUser);
複製程式碼

5、Null、日期格式化等Gson配置

Gson gson = new GsonBuilder()
            //格式化輸出
            .setPrettyPrinting()
            //序列化null
            .serializeNulls()
            // 設定日期時間格式,另有2個過載方法
            // 在序列化和反序化時均生效
            .setDateFormat("yyyy-MM-dd")
            // 禁此序列化內部類
            .disableInnerClassSerialization()
            //生成不可執行的Json(多了 )]}' 這4個字元)
            .generateNonExecutableJson()
            //禁止轉義html標籤
            .disableHtmlEscaping()
            .create();

User user = new User("John",30);
user.setDate(new Date()); //日期格式化
Log.i("format",gson.toJson(user));
複製程式碼

二、手動流式序列化和流式反序列化

利用JsonReaderJsonWriter手動讀取

JsonReader讀取:

try {
    JsonReader jsonReader = new JsonReader(new StringReader("[{\"name\":\"Cap\",\"age\":70, \"address\":\"xxx@163.com\"}" +
            ",{\"name\":\"Thor\",\"age\":1000, \"address\":\"xxx@q.com\"}]"));
    jsonReader.beginArray();
    while (jsonReader.hasNext()){
    jsonReader.beginObject();
    Log.d("JsonReaderArray",jsonReader.nextName()+":"+jsonReader.nextString());
    Log.d("JsonReaderArray",jsonReader.nextName()+":"+jsonReader.nextInt());
    Log.d("JsonReaderArray",jsonReader.nextName()+":"+jsonReader.nextString());
    jsonReader.endObject();
    }

    jsonReader.endArray();
    }catch (Exception e){
        e.printStackTrace();
    }
複製程式碼

JsonWriter讀取:

try {
    StringWriter writer = new StringWriter();
    JsonWriter jsonWriter = new JsonWriter(writer);        jsonWriter.beginArray();
    jsonWriter.beginObject();
    jsonWriter.name("name")
            .value("Stark")
            .name("age")
            .value(50)
            .name("email")
            .value("xxx@qq.com");
    jsonWriter.endObject();

    jsonWriter.beginObject();
    jsonWriter.name("name")
            .value("BlackWidow")
            .name("age")
            .value(40)
            .name("email")
            .value("xxx@qq.com");
    jsonWriter.endObject();
    jsonWriter.endArray();
    jsonWriter.flush();
    Log.i("JsonWriter","writer:"+writer.toString());
}catch (Exception e){
    e.printStackTrace();
}
複製程式碼

: beginObject和endObject配套使用,beginArray和endArray配套使用

三、過濾忽略欄位

需要使用new GsonBuilder()配置生成Gson物件。對於Gson gson = new Gson()直接生成的Gson物件不起作用。

1.@Expose註解

有兩個屬性值deserialize預設為true,serialize預設為true。定義在欄位上,表明是否在序列化或反序列化過程中暴露出來。

注:不匯出的不加

@Expose 
@Expose(deserialize = true,serialize = true) //序列化和反序列化都都生效,等價於上一條
@Expose(deserialize = true,serialize = false) //反序列化時生效
@Expose(deserialize = false,serialize = true) //序列化時生效
@Expose(deserialize = false,serialize = false) // 和不寫註解一樣
複製程式碼

User配置如上文 POJO類:User 所示;

Gson gson = new GsonBuilder()
        .excludeFieldsWithoutExposeAnnotation()//Expose生效
//      .serializeNulls()
//      .setPrettyPrinting()
        .create();
        
User user = new User("Tom",50);
User child = new User("Kid",6);
User parent = new User("parent",80);
List<User> children = new ArrayList<>();
children.add(child);
user.setChildren(children);
user.setParent(parent);
Log.d("expose","User:"+gson.toJson(user));
Log.i("expose","User:"+gson.fromJson("{\"age\":50,\"children\":[{\"age\":6,\"name\":\"Kid\"}],\"name\":\"Tom\",\"parent\":{\"age\":80,\"name\":\"parent\"}}"
        ,User.class));
複製程式碼

2.版本號(@Since和@Until)

使用方法:當前版本(GsonBuilder中設定的版本) 大於等於Since的值時該欄位匯出,小於Until的值時該該欄位匯出。

User配置如上文 POJO類:User 所示;

    Gson gson = new GsonBuilder()
            .setVersion(4) //設定版本號
            .create();
    User user = new User("Tom",50);
    User child = new User("Kid",6);
    User parent = new User("parent",80);
    List<User> children = new ArrayList<>();
    children.add(child);
    user.setChildren(children);
    user.setParent(parent);

    Log.d("version","User:"+gson.toJson(user));
    Log.i("version","User:"+gson.fromJson("{\"age\":50,\"children\":[{\"age\":6,\"name\":\"Kid\"}],\"name\":\"Tom\",\"parent\":{\"age\":80,\"name\":\"parent\"}}"
            ,User.class));
複製程式碼

3.許可權修飾符

public static final private protected這些修飾符

使用方法:

class ModifierSample {
    final String finalField = "final";
    static String staticField = "static";
    public String publicField = "public";
    protected String protectedField = "protected";
    String defaultField = "default";
    private String privateField = "private";
}
複製程式碼
ModifierSample modifierSample = new ModifierSample();
Gson gson = new GsonBuilder()
        .excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
        .create();
System.out.println(gson.toJson(modifierSample));
// 結果:{"publicField":"public","protectedField":"protected","defaultField":"default"}
複製程式碼

4.自定義過濾策略(最靈活方便)

基於GsonBuilderaddSerializationExclusionStrategyaddDeserializationExclusionStrategy,使用如下:

Gson gson = new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy() {
            //基於欄位排除
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
//                        Expose expose = f.getAnnotation(Expose.class);
//                        return expose!=null && !expose.serialize();
                return false;
        }

            //基於類排除
            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return clazz == int.class||clazz== Integer.class;
            }
    }).create();
複製程式碼

四、POJO與JSON欄位名的對映規則

GsonBuilder提供了FieldNamingStrategy介面和setFieldNamingPolicysetFieldNamingStrategy兩個方法,以及GSON的@SerializedName註解。

1.@SerializedName註解

@SerializedName註解擁有最高優先順序,在加有@SerializedName註解的欄位上FieldNamingStrategy不生效!

@SerializedName可以更改欄位名,還能提供備用名屬性:alternate,使用如下:

@SerializedName(value = "email_address",alternate = {"emailAddress","email"})
複製程式碼

2.FieldNamingPolicy

結合GsonBuilder.setFieldNamingPolicy使用,對於:

User user = new User("Cap", 70);
user.emailAddress = "xxx@163.com";
複製程式碼
Gson gson = new GsonBuilder()
    .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES)
    .create();
複製程式碼

對應的欄位效果是:

FieldNamingPolicy 結果(僅輸出emailAddress欄位)
IDENTITY {"emailAddress":"xxx@163.com"}
LOWER_CASE_WITH_DASHES {"email-address":"xxx@163.com"}
LOWER_CASE_WITH_UNDERSCORES {"email_address":"xxx@163.com"}
UPPER_CAMEL_CASE {"EmailAddress":"xxx@163.com"}
UPPER_CAMEL_CASE_WITH_SPACES {"Email Address":"xxx@163.com"}

3.自定義實現

GsonBuilder.setFieldNamingStrategy 方法需要與Gson提供的FieldNamingStrategy介面配合使用,用於實現將POJO的欄位與JSON的欄位相對應。

注:FieldNamingPolicy也實現了FieldNamingStrategy介面,即FieldNamingPolicy也可以使用setFieldNamingStrategy方法。

使用:

Gson gson = new GsonBuilder()
        .setFieldNamingStrategy(new FieldNamingStrategy() {
            @Override
            public String translateName(Field f) {
                //實現自己的規則
                return null;
            }
        })
        .create();
複製程式碼

五、接管序列化和反序列化過程

使用TypeAdapter介面,或JsonSerializerJsonDeserializer

TypeAdapter 以及 JsonSerializerJsonDeserializer 都需要與 GsonBuilder.registerTypeAdapterGsonBuilder.registerTypeHierarchyAdapter配合使用;

:註冊了 TypeAdapter之後,@SerializedName 、FieldNamingStrategy、Since、Until、Expose都失去了效果,直接由TypeAdapter之後接管。

registerTypeAdapterregisterTypeHierarchyAdapter的區別:

registerTypeAdapter registerTypeHierarchyAdapter
支援泛型
支援繼承

1.TypeAdapter

使用:

Gson gson  =new GsonBuilder().registerTypeAdapter(User.class, new TypeAdapter<User>() {
        @Override
        public void write(JsonWriter out, User value) throws IOException {
            out.beginObject();
            out.name("mName")
                    .value(value.getName());
            out.endObject();
            out.flush();
        }

        @Override
        public User read(JsonReader in) throws IOException {
            User user = new User("a",4);
            in.beginObject();
            if (in.nextName().equals("mName"))
                user.setName(in.nextString());
            in.endObject();
            return user;
        }
    }).create();

    User user = new User("我",10);
    Log.i("adapter","user:"+gson.toJson(user));
    Log.i("adapter","user:"+gson.fromJson("{\"mName\":\"我\"}",User.class));
複製程式碼

2.JsonSerializer和JsonDeserializer

TypeAdapter接管了序列化和反序列化過程,而JsonSerializerJsonDeserializer則只分別單獨接管序列化和反序列化過程。

使用:

    Gson gson  =new GsonBuilder()
        .registerTypeAdapter(User.class, new JsonSerializer<User>() {
            @Override
            public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
                JsonObject jsonElement = new JsonObject();
                jsonElement.addProperty("name","serialize");
                return jsonElement;
            }
    })
        .registerTypeAdapter(User.class, new JsonDeserializer<User>() {
            @Override
            public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                User user = new User("",10);
                user.setName(json.getAsJsonObject().get("mName").getAsString());
                return user;
            }
    }).create();
複製程式碼

3.TypeAdapterFactory

用於建立TypeAdapter的工廠類,通過對比Type,確定有沒有對應的TypeAdapter,沒有就返回null,與GsonBuilder.registerTypeAdapterFactory配合使用。

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new TypeAdapterFactory() {
        @Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            return null;
        }
    })
    .create();
複製程式碼

4.@JsonAdapter註解

作用在POJO類上的,接收一個引數,且必須是TypeAdpater,JsonSerializer或JsonDeserializer這三個其中之一。

前面的JsonSerializerJsonDeserializer都要配合GsonBuilder.registerTypeAdapter使用,但每次使用都要註冊也太麻煩了,JsonAdapter就是為了解決這個痛點的。

使用:

@JsonAdapter(UserTypeAdapter.class)
public final class User {
    .....
}
複製程式碼
public class UserTypeAdapter implements JsonSerializer<User> {

    @Override
    public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject jsonElement = new JsonObject();
        jsonElement.addProperty("name","serialize");
        return jsonElement;
    }
}
複製程式碼
 User user = new User("我",10);
Log.i("adapter","user:"+new Gson().toJson(user));//直接使用簡單的new GSON()即可,無需配置註冊
複製程式碼

參考連結

  1. Github UserGuide
  2. 怪盜kidou Gson系列文章