關於Android數據解析的容錯,你作過這些麼?

關於Android數據容錯,你作過這些麼?

Android開發者解析服務端返回的數據時候,通常會使用Gson/FastJson/moshi等數據解析框架。例如筆者項目中用的就是Gson,可是大多數使用者包括我以前都只是停留在使用的階段。java

Gson的toJson() 和 fromJson() 這兩個方法是Gson的最基本的使用方式,當被問及Gson如何對Json數據容錯,如何靈活序列化和反序列化的時候,就有點懵。android

Json數據容錯,最簡單的方式是讓先後端數據保持一致,就根本不存在容錯的問題,可是現實場景並不如咱們預期那麼好。json

舉幾個例子:User類中的姓名,有的接口返回 name ,有的接口返回 username ,如何容錯呢? age 字段返回的是 「18」 這樣的字符串,而java對象將其解析成 Int 類型的時候,Gson 有必定的類型容錯性,可以解析成功,可是若是 age 字段的返回值變成了 「」 、null呢? 如何讓其不拋出異常,而且設置默認值爲 0?後端

接下來就詳細看看,Gson是如何對數據作容錯解析。api

常規的使用,除了 toJson() 將 java 對象序列化成 Json 數據,或者 fromJson() 將 Json 數據反序列化成 java 對象,惟一須要注意的就是泛型擦除(下一篇講),針對泛型的解析,無非就是參數的差別。數據結構

Gson 的註解:

@SerializedName 和 @Expose框架

@SerializedName能夠用來配置 Json 字段的名字,最多見的場景就是不一樣語言的命名方式不統一,有的使用下劃線,有的使用駝峯命名。如 user_name 、userName、或者不規範的 username。那若是多個接口返回的數據結構一致,只有 fieldName 不一致,這樣就能夠經過 @SerializedName 來作容錯。ide

在 @SerializedName 中,還有一個 alternate 字段,用來對同一個字段配置多個解析名稱。ui

class User{
    @SerializedName(value = "user_name", alternate = arrayOf("name","username"))
    var userName :String? = null
    @SerializedName("user_age")
    var age = 0
}

再來看 @Expose ,它是用於指定一個字段是否參與序列化和反序列化, 可是一旦使用@Expose,那麼常規的 new Gson() 是不能生效的,須要使用 GsonBuilder 配合 .excludeFeildWithoutExposeAnnotation 方法使用,它有兩個配置項:serialize 和 deserialize ,用於指定序列化和反序列化是否包含此字段,默認值都是 truethis

class User{
    @SerializedName(value = "user_name",alternate = arrayOf("name","username"))
    @Expose
    var userName :String? = null
    @Expose
    var gender = 0
    var age = 0
    @Expose(serialize = true,deserialize = false)
    var genderDesc = ""
}

fun User.gsonTest(){
    // 序列化
    val user = User()
    user.userName = "xyd"
    user.age = 18
    user.gender = 1
    user.genderDesc = "男"

    val gson = GsonBuilder()
            .excludeFieldsWithoutExposeAnnotation()
            .create()

    val jsonStr = gson.toJson(user)
    Log.d("xyd","json:$jsonStr")
    // json:{"gender":1,"genderDesc":"男","user_name":"xyd"}

    val newUser = gson.fromJson(jsonStr,User::class.java)
    Log.d("xyd","genderDesc:${newUser.genderDesc}")
    // genderDesc:
}

能夠看到,genderDesc 只參與了序列化,而未參與反序列化。

注意: 一旦使用了 @Expose 後,全部的字段都必需要顯式的標記,不然不參與序列化和反序列化。

Gson 中的許多問題能夠經過這兩個重要的註解解決,可是更靈活的處理方式就須要進階了。

GsonBuilder 進階

前面瞭解到,想要構造一個 Gson 對象,有兩種方式:new Gson() 或 利用 GsonBuilder 構造。

例如,默認狀況下, Gson 是不會解析 null 字段的,而咱們經過 serializeNulls() 方法,來讓 Gson 序列化 null 字段。

val user = User()
user.age = 18
user.gender = 1

// 序列化
val jsonStr = GsonBuilder().create().toJson(user)
Log.d("xyd","json:$jsonStr")
// json:{"age":18,"gender":1}

// 反序列化
val jsonStr1 = GsonBuilder().serializeNulls().create().toJson(user)
Log.d("xyd","json1:$jsonStr1")
// json1:{"age":18,"gender":1,"userName":null}

GsonBuild 提供了更多的操做:

  • .serializeNulls() 序列化 null 字段
  • .setDateFormat() 設置日期格式,如 setDateFormate("yyyy-MM-dd")
  • .disableInnerClassSerialization: 禁止序列化內部類
  • .generateNonExcutableJson():生成不可直接解析的 JSON,會多 )]}' 這 4 個字符。
  • .disableHtmlEscaping():禁止轉移 HTML 標籤
  • .setPrettyPrinting():格式化輸出

不管是註解仍是 GsonBuilder 中提供的一些方法,都是 Gson 針對一些特殊場景爲咱們提供的便捷 API,更復雜的就不能解決了。

TypeAdapter

若是前面介紹的規則都知足不了業務,Gson 還有更進一步的處理方式,那就是使用 TypeAdapter。這個 TypeAdapter 實際是 Object 類型,也就是一個泛指。

使用 TypeAdapter 就須要用到 GsonBuilder 中的 registerTypeAdapter() 方法。

public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
    $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
        || typeAdapter instanceof JsonDeserializer<?>
        || typeAdapter instanceof InstanceCreator<?>
        || typeAdapter instanceof TypeAdapter<?>);
    if (typeAdapter instanceof InstanceCreator<?>) {
      instanceCreators.put(type, (InstanceCreator) typeAdapter);
    }
    if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
      TypeToken<?> typeToken = TypeToken.get(type);
      factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
    }
    if (typeAdapter instanceof TypeAdapter<?>) {
      factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
    }
    return this;
  }

能夠看到註冊方法,須要指定一個數據類型,且除了支持 TypeAdapter ,還支持 JsonSerializer JsonDeserializer。他們的區別是什麼?

TypeAdapter(抽象類)、JsonSerializer(接口)、JsonDeserializer(接口)。

TypeAdapter 中包含兩個主要的方法 write() 和 read() 方法,分別用於接管序列化和反序列化。

JsonSerializer 只用來接管序列化。

JsonDeserializer 只用來接管反序列化。

舉個使用實例:

val user = User()
user.age = 18
user.gender = 1
// 反序列化
val jsonStr = """ {"name":"xyd","age":18,"gender":""} """
val newUser = GsonBuilder().create.fromJson(jsonStr, User:class.java)
Log.e("xyd", "gender")

上面的例子中,gender 應該是一個 Int 值,而 Json 字符串中是 "" , 這樣的代碼跑起來就會直接報錯。怎麼處理呢?咱們經過實現 JsonDeserializer 接口,來接管反序列化的操做。

class IntegerDefaultAdapter : JsonDeserializer<Int>{
    override fun deserialize(json : JsonElement?, typeOfT : Type?, context : JsonDeserializationContext?){
        try{
            return json!!.getAsInt()
        }catch(e : NumberFormatException){
            return 0
        }
    }
}

這樣當轉換 Int 出現異常時,返回默認值 0。而後使用 registerTypeAdapter() 方法加入其中。

val newUser = GsonBuilder()
        .registerTypeAdapter(Int::class.java, IntegerDefault0Adapter())
        .create().fromJson(jsonStr,User::class.java)
Log.i("xyd","gender : ${newUser.gender}")
// gender : 0

TypeAdapter 的使用,到這裏就介紹完了。

相關文章
相關標籤/搜索