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 是同技术栈下的最优解之一。

相关推荐
bugcome_com5 小时前
JWT 知识小课堂:从入门到精通,一文吃透 JSON Web Token
前端·json
ccino .19 小时前
awvs_json_word.py 使用说明手册
json·word
richard_yuu19 小时前
JSON vs XML vs 二进制:3种序列化终极选型
xml·c++·qt·学习·json
用户7783366132111 天前
SERP 返回 JSON 用 Pydantic 强校验:类型安全解析
json
灵析表格2 天前
灵析表格财务函数深度实用性分析与实操教程
开发语言·ai·json·excel·wps
青 春 记 忆2 天前
零基础入门python07:让程序记住数据——JSON文件和异常处理
开发语言·windows·python·json·python3.11
2603_965148112 天前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api
鬼手点金2 天前
Scrapy 网络爬虫框架
爬虫·python·scrapy·ajax·html·json·requsts
鬼手点金3 天前
Scrapy + Playwright 完整示例(JS 动态渲染网页)
开发语言·javascript·爬虫·python·scrapy·html·json
AlfredZhao3 天前
VS Code 一键美化 JSON:不用安装插件
json