zoukankan      html  css  js  c++  java
  • 关于Android数据解析的容错,你做过这些么?

    Android开发者解析服务端返回的数据时候,一般会使用Gson/FastJson/moshi等数据解析框架。例如笔者项目中用的就是Gson,但是大多数使用者包括我之前都只是停留在使用的阶段。

    Gson的toJson() 和 fromJson() 这两个方法是Gson的最基本的使用方式,当被问及Gson如何对Json数据容错,如何灵活序列化和反序列化的时候,就有点懵。

    Json数据容错,最简单的方式是让前后端数据保持一致,就根本不存在容错的问题,但是现实场景并不如我们预期那么好。

    举几个例子:User类中的姓名,有的接口返回 name ,有的接口返回 username ,如何容错呢? age 字段返回的是 “18” 这样的字符串,而java对象将其解析成 Int 类型的时候,Gson 有一定的类型容错性,能够解析成功,但是如果 age 字段的返回值变成了 “” 、null呢? 如何让其不抛出异常,并且设置默认值为 0?

    接下来就详细看看,Gson是如何对数据做容错解析。

    常规的使用,除了 toJson() 将 java 对象序列化成 Json 数据,或者 fromJson() 将 Json 数据反序列化成 java 对象,唯一需要注意的就是泛型擦除(下一篇讲),针对泛型的解析,无非就是参数的差异。

    Gson 的注解:

    @SerializedName 和 @Expose

    @SerializedName可以用来配置 Json 字段的名字,最常见的场景就是不同语言的命名方式不统一,有的使用下划线,有的使用驼峰命名。如 user_name 、userName、或者不规范的 username。那如果多个接口返回的数据结构一致,只有 fieldName 不一致,这样就可以通过 @SerializedName 来做容错。

    在 @SerializedName 中,还有一个 alternate 字段,用来对同一个字段配置多个解析名称。

    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 ,用于指定序列化和反序列化是否包含此字段,默认值都是 true

    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 的使用,到这里就介绍完了。

  • 相关阅读:
    最大子数组问题(分治策略实现)
    Solving the Detached Many-to-Many Problem with the Entity Framework
    Working With Entity Framework Detached Objects
    Attaching detached POCO to EF DbContext
    如何获取qq空间最近访问人列表
    Health Monitoring in ASP.NET 2.0
    problem with displaying the markers on Google maps
    WebMatrix Database.Open… Close() and Dispose()
    Accessing and Updating Data in ASP.NET: Retrieving XML Data with XmlDataSource Control
    Create web setup project that has crystal reports and sql script run manually on client system
  • 原文地址:https://www.cnblogs.com/brin/p/11468297.html
Copyright © 2011-2022 走看看