Kotlin反序列化空值以及默认值轻松搞定

1,459 阅读4分钟

前言

本文章只讨论解决空值以及默认值问题,不做框架中序列化和序列化的所使用方法以及优劣性进行分析,如有需要请自己研究

问题描述

我们平常在做网络请求时候,当后台服务器需要返回一个用户数据的时候,假如返回的是 username 和 phoneNumber,根据这个我们轻松写出下面的对象类:

data class UserBean(val userName:String,val phoneNumber:String)

用做来网络解析美滋滋,瞬间就可以Ok,想着可以提测上线早下班,然而还是想少了,想不到服务器不靠谱,直接给你返回一个:

{"userName":"User1"}

然后你发现你炸了,直接解析异常或者闪退了,这时候你气冲冲的找服务器理论,为啥电话号码字段没有了

  • 服务器:产品要求的,电话号码可以为空。
    
  • 你:那你应该返回这个字段啊! 
    
  • 服务器:都为空了我返回干嘛,你自己解决。
    

听到这你也只能自己解决了,修改一下变成以下代码,为了防止其他字段为空,你就要把所有代码都加上可空的标识

data class UserBean(val userName:String?,val phoneNumber:String?)

这样虽说解决了问题,但是在参数比较多的时候,你会发现全是?,而且在使用的时候各种判断空 至于为啥要全部加?就是因为服务器实在是信不过,直接给你来个幺儿子

解决办法

鉴于这种情况,谷歌原生其实有专门的框架可以解决 kotlinx-serialization ,但是kotlinx-serialization需要注解,而且只支持kt,使用起来会稍微麻烦一点。 既然说是王者归来,那肯定不是用kotlinx-serialization解决了,今天的主角是Moshi,你没听错,就是Okhttp公司出品的Moshi,前两年我用Moshi的时候,也还是和kotlinx-serialization一样需要用注解,但现在已经完全不需要了,用起来及其方便,而且完全解决问题

重要的事情说三遍:不需要注解,不需要注解,不需要注解,直接服用

下面会写出kotlinx-serialization和Moshi 还有 FastJson序列化已经反序列的用法,结果请自己验证一下,只有moshi和kotlinx-serialization支持默认值的,不过kotlinx-serialization需要注解,moshi不需要

在写代码前先抽离一下序列化和反序列化的方法,以及测试的方法,后面替换可以直接用:

/**
 *序列化拓展函数
 **/
fun Any?.toJson(): String {
    return ""
}
/**
 *反序列化拓展函数
 **/
fun String?.fromJsonString(): T? {
    return null
}

验证的函数

data class TestBean(var test1: String = "", val test2: String = "")

fun testFun(){
    val testBean = TestBean("testStr1")
    testBean.toJson().logE("测试序列化")
    "{"test1":"111"}".fromJsonString<TestBean>().logE("测试反序列化,没字段")
    "{}".fromJsonString<TestBean>().logE("测试反序列化,测试都没有值")
}


Moshi

导包

implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'

修改一下上面的序列化函数

val moshi by lazy {
    Moshi.Builder()
        .addLast(KotlinJsonAdapterFactory())
        .build()
}
/**
 *序列化拓展函数
 **/
fun Any?.toJson(): String {
    return try {
    moshi.adapter(this::class.java).toJson(this)
    } catch (e: Throwable) {
    e.log()
    ""
    }
    }
/**
 *反序列化拓展函数
 **/
fun String?.fromJsonString(): T? {
    return try {
    if (this.isNullOrBlank()) null else {
        moshi.adapter(T::class.java).fromJson(this)
    }
    } catch (e: Throwable) {
    e.log()
    null
    }    
}

可以快乐去验证了

kotlinx-serialization

导包

//根目录的build.gradle添加
classpath  "org.jetbrains.kotlin:kotlin-serialization:1.6.0"

//项目build.gradle添加
id  'kotlinx-serialization'

implementation="org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"

再次修改上面的序列化函数


val jsonDecoder = Json {
    prettyPrint = false
    isLenient = true
    ignoreUnknownKeys = true // JSON和数据模型字段可以不匹配
    coerceInputValues = true // 如果JSON字段是Null则使用默认值
    allowStructuredMapKeys = true //启用Map序列化(默认情况是不能序列化Map)\
    explicitNulls = true
    encodeDefaults = true
}
/**
 *序列化拓展函数
 **/
fun Any?.toJson(): String {
    return try {
         jsonDecoder.encodeToString(this)
    } catch (e: Throwable) {
    e.log()
    ""
    }
    }
/**
 *反序列化拓展函数
 **/
fun String?.fromJsonString(): T? {
    return try {
    if (this.isNullOrBlank()) null else {
         jsonDecoder.decodeFromString(this)
    }
    } catch (e: Throwable) {
    e.log()
    null
    }    
}

最重要的,要加注解,要不然会报异常

@Serializable
data class TestBean(var test1: String = "", val test2: String = "")

可以快乐去验证了

FastJson

fastjson已经出FastJson2了,不过还是很多bug,建议还是使用1

修改上面的方法

/**
 *序列化拓展函数
 **/
fun Any?.toJson(): String {
    return try {
        JSON.toJSONString(this)
    } catch (e: Throwable) {
    e.log()
    ""
    }
    }
/**
 *反序列化拓展函数
 **/
fun String?.fromJsonString(): T? {
    return try {
    if (this.isNullOrBlank()) null else {
       JSON.parseObject(this,object : TypeReference<T>() {})
    }
    } catch (e: Throwable) {
    e.log()
    null
    }    
}

去快乐的验证吧