Kotlin Moshi库 全面使用指南

在 Android 与 JVM 生态的 JSON 解析方案中,Square 出品的 Moshi 凭借对 Kotlin 的良好适配、轻量高效的特性,成为很多项目替代 Gson 的首选。它既保留了简洁的 API 设计,又提供了编译期代码生成与反射两种解析模式,尤其适配 Kotlin 的空安全、data class 与默认参数特性,同时与 OkHttp、Retrofit 等 Square 技术栈无缝衔接。

本文将从环境配置、基础用法、进阶场景到踩坑指南,系统讲解 Moshi 在 Kotlin 项目中的完整使用方式。

如果想把JSON字符串转Kotlin或者Java可以使用下面的网站,支持添加Moshi,Gson等注解 devfy9.com/zh/json

一、环境准备:两种接入模式

Moshi 针对 Kotlin 提供了两种使用模式:运行时反射模式与 KSP 编译期代码生成模式。前者接入简单,后者性能更优、无反射开销,是生产环境的推荐方案。

1. 核心依赖

两种模式都需要引入 Moshi 核心库,当前稳定版本以 1.15.1 为例:

kotlin 复制代码
// 模块级 build.gradle.kts
dependencies {
    implementation("com.squareup.moshi:moshi:1.15.1")
}

2. 反射模式(快速接入)

引入 Kotlin 反射适配器后,无需额外注解即可直接解析 data class。缺点是依赖 kotlin-reflect,会增加应用包体积,且运行时反射存在固定性能损耗,适合小型项目或快速原型开发。

kotlin 复制代码
dependencies {
    implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
}

3. KSP 代码生成模式(生产推荐)

通过 KSP 在编译期生成 JsonAdapter 类,完全避免运行时反射,解析速度更快,也无需依赖反射库,是官方推荐的生产级方案。

第一步:在项目根 build.gradle.kts 中添加 KSP 插件(版本需与 Kotlin 版本匹配)

kotlin 复制代码
plugins {
    id("com.google.devtools.ksp") version "1.9.20-1.0.14" apply false
}

第二步:模块级引入 KSP 插件与 Moshi 代码生成依赖

kotlin 复制代码
plugins {
    id("com.google.devtools.ksp")
}

dependencies {
    implementation("com.squareup.moshi:moshi:1.15.1")
    ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.1")
}

二、基础使用:序列化与反序列化

1. 定义数据类

使用 KSP 模式时,需要给 data class 添加 @JsonClass(generateAdapter = true) 注解,通知编译器生成对应的解析适配器。

kotlin 复制代码
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class User(
    val id: Int,
    val username: String,
    val email: String?,
    val age: Int = 18 // Kotlin 默认参数可正常生效
)

2. 初始化 Moshi 实例

Moshi 实例内部维护了适配器缓存,全局单例复用即可,避免重复创建带来的性能开销。

kotlin 复制代码
val moshi: Moshi = Moshi.Builder()
    // 反射模式必须添加;纯 KSP 模式可省略
    // .add(KotlinJsonAdapterFactory())
    .build()

如果项目中同时存在 KSP 生成适配器与反射解析的类,则必须添加 KotlinJsonAdapterFactory 作为兜底。

3. JSON 反序列化(字符串转对象)

通过 adapter() 获取对应类型的解析器,再调用 fromJson 完成解析:

kotlin 复制代码
val json = """{"id":1,"username":"ZhangSan","email":"zhangsan@example.com"}"""

val userAdapter = moshi.adapter(User::class.java)
val user: User? = userAdapter.fromJson(json)

println(user?.username) // 输出 ZhangSan
println(user?.age)      // 输出 18,字段缺失时默认参数生效

4. JSON 序列化(对象转字符串)

调用 toJson 即可将对象转为 JSON 字符串:

kotlin 复制代码
val user = User(id = 2, username = "LiSi", email = null)
val json: String = userAdapter.toJson(user)

println(json)
// 输出:{"id":2,"username":"LiSi","email":null}

5. 字段名映射

当服务端返回字段命名风格与 Kotlin 驼峰规范不一致时,使用 @Json 注解指定字段映射关系:

kotlin 复制代码
@JsonClass(generateAdapter = true)
data class Article(
    val id: Int,
    @Json(name = "article_title")
    val articleTitle: String,
    @Json(name = "create_time")
    val createTime: Long
)

三、进阶场景:泛型、自定义与容错

1. 解析集合与泛型类型

受 Java 泛型擦除机制影响,无法直接通过 List<User>::class.java 获取集合类型,需要借助 Types.newParameterizedType 构造参数化类型:

kotlin 复制代码
import com.squareup.moshi.Types

val userListJson = """
    [{"id":1,"username":"A"},{"id":2,"username":"B"}]
""".trimIndent()

// 构造 List<User> 类型
val listType = Types.newParameterizedType(List::class.java, User::class.java)
val listAdapter = moshi.adapter<List<User>>(listType)

val userList: List<User>? = listAdapter.fromJson(userListJson)

2. 自定义类型适配器

对于日期、枚举、自定义类等特殊类型,可以通过继承 JsonAdapter 实现定制化的序列化与反序列化逻辑。以时间戳转 LocalDateTime 为例:

kotlin 复制代码
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId

class LocalDateTimeAdapter : JsonAdapter<LocalDateTime>() {
    override fun fromJson(reader: JsonReader): LocalDateTime? {
        val timestamp = reader.nextLong()
        return Instant.ofEpochMilli(timestamp)
            .atZone(ZoneId.systemDefault())
            .toLocalDateTime()
    }

    override fun toJson(writer: JsonWriter, value: LocalDateTime?) {
        value?.let {
            writer.value(it.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())
        } ?: writer.nullValue()
    }
}

将自定义适配器注册到 Moshi 中,全局生效:

kotlin 复制代码
val moshi = Moshi.Builder()
    .add(LocalDateTimeAdapter())
    .build()

3. 容错与严格模式

  • 默认行为:JSON 中包含实体类未定义的字段时,Moshi 会自动忽略,不会抛出异常,兼容性更强。
  • 严格模式:若需要在联调阶段校验接口字段,可开启未知字段报错:
kotlin 复制代码
val moshi = Moshi.Builder()
    .failOnUnknown() // 遇到未知字段直接抛出异常
    .build()

四、Kotlin 生态适配:Retrofit 集成与空安全

1. 与 Retrofit 联合使用

Moshi 是 Retrofit 官方适配的解析器之一,引入对应转换器即可完成无缝接入,是 Android 网络请求场景的经典组合。

kotlin 复制代码
dependencies {
    implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
}

构建 Retrofit 实例时传入 Moshi 转换器:

kotlin 复制代码
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .build()

2. Kotlin 空安全特性

Moshi 严格遵守 Kotlin 的空安全规则,这也是它相比 Gson 最核心的优势之一:

  • 字段声明为非空类型时,若 JSON 中该字段为 null 或缺失,Moshi 会直接抛出解析异常,从源头避免隐性空指针。
  • 字段声明为可空类型时,正常解析 null 值。
  • 配合 Kotlin 默认参数,字段缺失时会自动使用默认值填充,不会触发空安全异常。

而 Gson 会通过反射绕过 Kotlin 空安全检查,给非空字段赋值 null,容易在运行期埋下难以排查的崩溃隐患。

五、常见踩坑与最佳实践

  1. 忘记添加 @JsonClass 注解 使用 KSP 模式时,若 data class 遗漏 @JsonClass(generateAdapter = true),Moshi 会回退到反射模式解析;若项目未引入反射库,则会直接抛出异常。

  2. 混淆配置 纯 KSP 代码生成模式下,不需要额外配置混淆规则;若使用反射模式,需要在 ProGuard 中保留数据类成员与 Kotlin 反射相关类,避免解析失败。

  3. 复杂泛型嵌套解析 对于 Map<String, List<User>> 这类多层嵌套泛型,可通过 Types.newParameterizedType 嵌套构造类型:

kotlin 复制代码
val mapType = Types.newParameterizedType(
    Map::class.java,
    String::class.java,
    Types.newParameterizedType(List::class.java, User::class.java)
)
  1. 选型边界
  • 纯 Kotlin Android 项目、Square 技术栈:优先 KSP 模式 Moshi,兼顾性能与空安全。
  • KMM 跨平台项目:Moshi 仅支持 JVM,应选择 kotlinx.serialization。
  • 后端 Spring 生态:Jackson 功能更完善、适配更广,优先选择。

总的来说,Moshi 是 Kotlin JVM/Android 场景下非常均衡的 JSON 解析方案,它既解决了 Gson 在 Kotlin 环境下的空安全隐患,又保持了轻量易用的特性,配合 KSP 代码生成可以在性能上达到最优。对于基于 Retrofit + OkHttp 的 Android 项目,Moshi 是同技术栈下的最优解之一。

相关推荐
海兰1 天前
【高速缓存】RedisVL 存储类型选择指南:Hash 与 JSON
人工智能·redis·算法·缓存·json·哈希算法
减瓦1 天前
Jackson 使用指南
java·spring boot·json
制造数据与AI践行者老蒋2 天前
离谱!PromptTemplate 遇上 JSON,花括号直接引发解析战争
数据库·microsoft·json
chenjingming6662 天前
JWT(JSON Web Token)有效期查看登录过期时间
json
绘梨衣的sakura路3 天前
Fastjson ≤ 1.2.83 新型 RCE 漏洞深度分析:三层绕过机制与完整利用链
安全·web安全·json
江湖十年3 天前
Go 语言中 YAML to JSON 踩坑笔记
后端·go·json
前网易架构师-高司机3 天前
带标注的扑克牌识别数据集,识别率99.5%,3083张图,支持yolo,coco json,voc xml,文末有模型训练代码
xml·yolo·json·数据集·数字·扑克牌·纸牌
前网易架构师-高司机3 天前
带标注的浮游藻类24种数据集,识别率91.5 %数据集, 23175张图,支持yolo,coco json,voc xml,文末有模型训练代码
yolo·json·数据集·微观·生物·浮游·藻类
维天说4 天前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json