vs Gson

Gson的仓库介绍:Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object.

Gson专门用于Java。

Moshi的介绍:Moshi is a modern JSON library for Android, Java and Kotlin. It makes it easy to parse JSON into Java and Kotlin classes.

在开始学习之前,有理由推测 Moshi针对Kotlin做了一些提升,可以做到Gson对Kotlin所做不到的功能。

依赖

1
2
3
4
5
6
7
8
9
10
// kts
plugins {
kotlin("jvm") version "1.8.10"
java
kotlin("kapt") version "1.8.10"
}
// Moshi
implementation("com.squareup.moshi:moshi:1.14.0")
implementation("com.squareup.moshi:moshi-kotlin:1.14.0")// 包含KotlinJsonAdapterFactory(),可使用反射的方式序列化Kotlin 类
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.14.0") // 使用codegen的方式序列化 Kotlin 类

反射和codegen必须使用其一或都使用。不然会报错。

  • 使用反射添加KotlinJsonAdapterFactory
  • 使用codegen在类添加 @JsonClass(generateAdapter = true)

使用反射或codegen的不同

基本使用

基本代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
data class BlackjackHand(
val hidden_card: Card,
val visible_cards: List<Card>,
)

data class Card(
val rank: Char,
val suit: Suit
)

enum class Suit {
CLUBS, DIAMONDS, HEARTS, SPADES;
}

val json = """
{
"hidden_card": {
"rank": "6",
"suit": "SPADES"
},
"visible_cards": [
{
"rank": "4",
"suit": "CLUBS"
},
{
"rank": "A",
"suit": "HEARTS"
}
]
}
""".trimIndent()

val moshi: Moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()

from json

1
2
3
4
5
6
7
// from json
fun main() {
val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter(BlackjackHand::class.java)
// val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter() 实验性
val blackjackHand = jsonAdapter.fromJson(json)
println(blackjackHand)
}

to json

1
2
3
4
5
6
7
8
9
10
11
12
// to json
fun main() {
val blackjackHand = BlackjackHand(
Card('6', Suit.SPADES),
listOf(Card('4', Suit.CLUBS), Card('A', Suit.HEARTS))
)

val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter(BlackjackHand::class.java)
// val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter() 实验性
val json: String = jsonAdapter.toJson(blackjackHand)
println(json)
}

parse array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val arrayJson = """
[
{
"rank": "4",
"suit": "CLUBS"
},
{
"rank": "A",
"suit": "HEARTS"
}
]
""".trimIndent()

val adapter: JsonAdapter<List<Card>> = moshi.adapter()// 实验性方法
val cards: List<Card>? = adapter.fromJson(arrayJson)
print(cards)
}

报错规则

Moshi always throws a standard java.io.IOException if there is an error reading the JSON document, or if it is malformed. It throws a JsonDataException if the JSON document is well-formed, but doesn’t match the expected format.

自定义字段名

1
2
3
4
5
6
class Player {
val username: String
@Json(name = "lucky number") val luckyNumber: Int

...
}

忽略字段

By default, all fields are emitted when encoding JSON, and all fields are accepted when decoding JSON. Prevent a field from being included by annotating them with @Json(ignore = true).

1
2
3
4
5
6
class BlackjackHand(...) {
@Json(ignore = true)
var total: Int = 0

...
}

These fields are omitted when writing JSON. When reading JSON, the field is skipped even if the JSON contains a value for the field. Instead, it will get a default value. In Kotlin, these fields must have a default value if they are in the primary constructor.

Note that you can also use Java’s transient keyword or Kotlin’s @Transient annotation on these fields for the same effect.

自定义适配器

https://github.com/square/moshi#custom-type-adapters

https://github.com/square/moshi#alternate-type-adapters-with-jsonqualifier

Retrofit2 Gson迁移至Moshi

如果没有自定义适配器和自定义字段名,直接更改就好

1
addConverterFactory(MoshiConverterFactory.create())

参考资料

掘金

moshi github readme

掘金2