Moshi三宗罪

Android一直使用的是Goolge的Gson,开始使用kotlin后发现Gson处理kotlin的data class会出现各种空指针,kotlin的非空校验失效;于是开始研究Moshi并使用它。 下面我会简单介绍下moshi的使用,并详细说明从Gson转到Moshi存在的我满意的地方。

使用介绍

Json解析有两种方式,一种是给Bean加注解,一种是使用kotlin的反射,为了简单我这里使用的是反射

  • 添加依赖
kotlin 复制代码
implementation("com.squareup.moshi:moshi-kotlin:1.15.2")
  • 使用
kotlin 复制代码
var moshi =
    Moshi.Builder()
        //Date-long转换
        .add(DateJsonAdapter())
        //moshi官方对kotlin类的处理
        .addLast(KotlinJsonAdapterFactory())
        .build()

fun <T> fromJson(json: String?, type: Type): T? {
    if (json.isNullOrBlank()) return null
    return moshi.adapter<T>(type).fromJson(json)
}

本人不满意的点

一、强制非空判断

json为null但字段非null时,报异常,但我希望的是如果为null则给个默认值,下面的例子中默认指定了val age: Int = 3,而json里是"age":null,解析结果是异常Expected an int but was NULL at path $.age

kotlin 复制代码
private fun testDataClass() {
    val json = """{"name":"wzw","age":null, "gender":1, "son":{}}"""
    //com.squareup.moshi.JsonDataException: Expected an int but was NULL at path $.age
    val fromBeanJson = MoshiUtil.fromBeanJson<DataWen>(json)
    println("moshi: $fromBeanJson")
}
data class DataWen(
    val name: String? = null,
    //如果为null则使用默认值,但moshi做不到
    val age: Int = 3,
    val gender: Int? = null
)

二、无法指定默认值

json为null但字段可null且有默认值,默认值失效,下面的例子中val age: Int? = 3 age可空且指定里默认值,但由于json里的age=null所以最终解析出来的age=null;这个问题和上面第一个问题类似,我希望的是null的时候能使用默认值,但是moshi做不到。

kotlin 复制代码
private fun testDataClass() {
    val json = """{"name":"wzw","age":null, "gender":1, "son":{}}"""
    val fromBeanJson = MoshiUtil.fromBeanJson<DataWen>(json)
    //DataWen(name=wzw, age=null, gender=1)
    println("moshi: $fromBeanJson")
}
data class DataWen(
    val name: String? = null,
    val age: Int? = 3,
    val gender: Int? = null
)

为什么我一定要纠结这个默认值?

因为:

  1. Gson支持默认值,当为null时可以使用默认值,达到对象不为null的效果;
  2. App无法保证后台接口返回的json一定是自己想要的,必须要有容错否则崩溃了就是移动端开发人员的锅;而Moshi的容错能力太差了

三、后台没返回该字段时报错

json没有该字段,但bean中定义了该字段非空,则报错Required value 'age' missing at $。 如下面的例子中,json里没有age字段,但DataWen里定义了非null的age:val age: Int,Moshi在解析时就会报错。

当然,要解决这个问题很简单,只要age给个默认值就可以,如val age: Int = 18。把这个问题列出来是想说Moshi有这个问题,使用时要注意!

kotlin 复制代码
private fun 测试dataClass() {
    val json = """{"name":"wzw", "gender":1, "son":{}}"""
    //com.squareup.moshi.JsonDataException: Required value 'age' missing at $
    val fromBeanJson = MoshiUtil.fromBeanJson<DataWen>(json)
    println("moshi: $fromBeanJson")
}
data class DataWen(
    val name: String? = null,
    val age: Int,
    val gender: Int? = null
)

四、繁琐或耗时等

Moshi的解析方式有两种,一种是通过注解,需要在每个类上加注解,听说解析速度比Gson快;另一种是通过反射,速度比Gson慢,尤其是第一次解析,甚至比Gson慢十几倍,而且反射需要引入kotlin的反射库,jar包2.5M左右;

  • 注解解析需要
kotlin 复制代码
//需要加注解
@JsonClass(generateAdapter = true)
data class DataWen(
    val name: String? = null,
    val age: Int = 3,
    val gender: Int? = null
)
  • 反射解析初次速度慢

这里一直强调初次/第一次是因为kotlin反射sdk第一次使用有一个初始化过程,这个过程耗时,但是即使第二次第三次使用,moshi的解析也不会比gson快,因为kotlin反射本来就比java的慢;---不只是理解的对不对😄

ini 复制代码
//耗时
gson: DataWen(name=wzw, age=null, gender=1) take time: 23 
moshi: DataWen(name=wzw, age=null, gender=1) take time: 301

总结

  • Moshi的容错能力不行,App如果要使用moshi,必须要注意上面这些问题,也可以自己写Adapter规避上面这些问题,但这个会影响性能;

  • 如果决定继续使用Gson,则不能使用kotlin的数据类data class,要改为普通的class,且要有默认空构造,且字段不能是val

kotlin 复制代码
data class DataWen(
    val name: String? = null,
    val age: Int,
    val gender: Int? = null
)
//改为
class DataWen(
    var name: String? = null,
    var age: Int,
    var gender: Int? = null
)
  • 以上列出的所有问题通过自定义Adapter都可以解决,就看你愿不愿意折腾;

要解决可参考:github上的moshi-nullsafe

相关推荐
伟大的大威1 小时前
Android 端离线语音控制设备管理系统:完整技术方案与实践
android·macos·xcode
骑驴看星星a4 小时前
【Three.js--manual script】4.光照
android·开发语言·javascript
TDengine (老段)11 小时前
TDengine 字符串函数 CONCAT_WS 用户手册
android·大数据·数据库·时序数据库·tdengine·涛思数据
会跑的兔子11 小时前
Android 16 Kotlin协程 第一部分
android·开发语言·kotlin
Meteors.12 小时前
安卓进阶——OpenGL ES
android
椰羊sqrt14 小时前
CVE-2025-4334 深度分析:WordPress wp-registration 插件权限提升漏洞
android·开发语言·okhttp·网络安全
2501_9160088914 小时前
金融类 App 加密加固方法,多工具组合的工程化实践(金融级别/IPA 加固/无源码落地/Ipa Guard + 流水线)
android·ios·金融·小程序·uni-app·iphone·webview
sun00770014 小时前
Android设备推送traceroute命令
android
来来走走14 小时前
Android开发(Kotlin) 高阶函数、内联函数
android·开发语言·kotlin
2501_9159214314 小时前
Fastlane 结合 开心上架(Appuploader)命令行版本实现跨平台上传发布 iOS App 免 Mac 自动化上架实战全解析
android·macos·ios·小程序·uni-app·自动化·iphone