你已经了解了将数据序列化为 JSON 格式进行传输的好处。手动转换类并不方便,不过幸运的是,有许多库能简化这个过程。在 Android 开发中,一个非常流行且常用的库叫做 Moshi。
Moshi 的 API 比其他知名库(如 Jackson 和 Gson)要小很多。Gson 有超过一千个方法,而 Moshi 只有一半。Moshi 体积也更小,只有 100KB 多,而 Gson 约为 300KB。
Moshi 速度快,占用内存少,是一个很好的选择。在本节中,你将学习如何使用它,同时进一步了解 JSON 的结构。
添加库依赖
要使用 Moshi 库,需要在 build.gradle.kts
文件的 dependencies
部分添加如下依赖:
implementation("com.squareup.moshi:moshi-kotlin:1.11.0")
代码说明
添加这行后,IDE 会在你编写代码时自动导入需要的类。
你可以访问 Moshi 的 GitHub 页面了解更多信息。
将 Kotlin 对象序列化为 JSON 字符串
首先定义一个 Human
类,用于演示操作:
class Human(var name: String, var age: Int, var friends: Array<String>)
代码说明
这是一个简单的类,包含姓名、年龄和朋友数组。
接下来创建 Human
类的实例:
val human = Human("Mike", 20, arrayOf("Alex", "Valery", "Ann"))
代码说明
创建了一个名为 Mike、20 岁,有三个朋友的对象。
使用 Moshi 需要通过 Builder 模式创建一个 Moshi 实例:
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
代码说明
这是 Moshi 的标准构造方式,KotlinJsonAdapterFactory()
用来支持 Kotlin 的特性。
接着,需要为 Human
类创建一个适配器,用于序列化和反序列化:
val humanAdapter = moshi.adapter(Human::class.java)
代码说明
Human::class.java
是 Kotlin 中获取类的 Java 反射对象的方式。
这样,我们就可以快速地将对象转换成 JSON 字符串了,比如:
print(humanAdapter.toJson(human))
// 输出: {"name":"Mike","age":20,"friends":["Alex","Valery","Ann"]}
代码说明
toJson()
方法把 human
对象转换成 JSON 格式的字符串。
将 JSON 反序列化为 Kotlin 对象
有了适配器后,我们可以用它来根据 JSON 字符串重新创建对象:
val newHumanString = """
{"name":"John",
"age":25,
"friends":["Mike","Helen"]}""".trimIndent()
val newHuman = humanAdapter.fromJson(newHumanString)
代码说明
fromJson
方法把 JSON 字符串转成 Human?
对象(注意是可空类型),因为解析可能失败,返回 null。
反序列化更复杂的数据结构 — 对象列表
如果要处理两个类的组合,比如 Human
和 List
,需要使用 ParameterizedType
:
val humanList = listOf(human, newHuman)
val type = Types.newParameterizedType(List::class.java, Human::class.java)
val humanListAdapter = moshi.adapter<List<Human?>>(type)
print(humanListAdapter.toJson(humanList))
// 输出: [{"name":"Mike","age":20,"friends":["Alex","Valery","Ann"]},{"name":"John","age":25,"friends":["Mike","Helen"]}]
代码说明
通过 Types.newParameterizedType
将 List
和 Human
组合起来创建适配器,方便处理列表。
也可以用它反序列化 JSON 字符串:
val jsonStr =
"""[{"name":"Nick","age":10,"friends":["Valery"]},
{"name":"John","age":25,"friends":[]},
{"name":"Kate","age":40,"friends":[]}]
""".trimIndent()
val newHumanList = humanListAdapter.fromJson(jsonStr)
操作反序列化后的 JSON 对象
反序列化后,可以轻松访问对象的属性:
print(newHuman?.name) // 输出: John
访问更复杂的数据:
print(newHuman?.friends.contentToString()) // 输出: [Mike, Helen]
遍历列表中所有对象:
for (h in newHumanList!!) {
print(h?.name + " ")
}
// 输出: Nick John Kate
这里我们用 !!
断言列表不为空。
增加 Map 类型字段
修改 Human
类,加入显示科目成绩的 Map
:
class Human(var name: String, var age: Int, var friends: Array<String>, var grades: Map<String, String>)
创建对应包含 Map 的适配器:
val type = Types.newParameterizedType(List::class.java, Human::class.java, Map::class.java)
val humanListAdapter = moshi.adapter<List<Human?>>(type)
然后像之前一样用 JSON 字符串反序列化:
val jsonStr =
"""[{"name":"Ruslan","age":20,"friends":["Victoria","Valery"],
"grades":{"Math":"A","Philosophy":"F","Physics":"D"}},
{"name":"Victoria","age":35,"friends":["Ruslan","Anastasia"],
"grades":{"Math":"B","Philosophy":"C","Physics":"B"}}]""".trimIndent()
val humanList = humanListAdapter.fromJson(jsonStr)
最后输出某人某科成绩:
for (h in humanList!!) {
println(h?.name + " has a grade of ${h!!.grades["Math"]} in maths")
}
// 输出:
// Ruslan has a grade of A in maths
// Victoria has a grade of B in maths
总结
本节你学习了 Moshi 库的优点,了解了如何利用它将 Kotlin 对象转成 JSON 字符串,及如何从 JSON 字符串反序列化回对象或对象列表,并且演示了如何操作反序列化得到的对象数据,同时加深了对 JSON 结构的理解。