Moshi 的 Kotlin codegen 有什么用?
What's the use of Moshi's Kotlin codegen?
我最近开始在我的 Android 应用程序中使用 Moshi,我很想知道更多关于注释 @JsonClass(generateAdapter = true)
really 的作用。
示例数据class:
data class Person(
val name: String
)
我能够 serialise/de-serialise 这个 class 如下:
val moshi: Moshi = Moshi.Builder().build()
moshi.adapter(Person::class.java).toJson(Person())
我没有在这里使用@JsonClass注解,因此codegen不会启动。
我的问题是,为什么以及什么时候需要使用 @JsonClass(generateAdapter = true)
早期版本的 Moshi 不支持 "codegen",因此它们完全依赖反射(即在运行时内省 classes 的能力)。但是,对于需要非常高性能的应用程序来说,这可能是个问题,因此他们添加了 "codegen" 功能来利用注释处理。基本上这允许在编译时生成代码,因此他们可以在不使用反射的情况下执行 serialisation/deserialisation。
为了启用代码生成功能,您还需要启用 Kapt,这是 Kotlin 注释处理器,否则将不会进行任何注释处理。
Here you'll find how to enable and configure Kapt, and here 您将了解如何设置 Moshi codegen 依赖项。
编辑
在您添加到问题的代码片段中,Moshi 使用 ClassJsonAdapter 序列化您的对象。该适配器利用反射来查找所有字段并创建 JSON 字符串(您可以看到 class 从 java.lang.reflect
导入内容)。
另一方面,Moshi code gen 为您的 class 生成一个 JsonAdapter。例如,让 Moshi 为您的 Person
class 创建适配器将生成以下内容:
// Code generated by moshi-kotlin-codegen. Do not edit.
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonDataException
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.Moshi
import java.lang.NullPointerException
import kotlin.String
class PersonJsonAdapter(moshi: Moshi) : JsonAdapter<Person>() {
private val options: JsonReader.Options = JsonReader.Options.of("name")
private val stringAdapter: JsonAdapter<String> =
moshi.adapter<String>(String::class.java, kotlin.collections.emptySet(), "name")
override fun toString(): String = "GeneratedJsonAdapter(Person)"
override fun fromJson(reader: JsonReader): Person {
var name: String? = null
reader.beginObject()
while (reader.hasNext()) {
when (reader.selectName(options)) {
0 -> name = stringAdapter.fromJson(reader) ?: throw JsonDataException("Non-null value 'name' was null at ${reader.path}")
-1 -> {
// Unknown name, skip it.
reader.skipName()
reader.skipValue()
}
}
}
reader.endObject()
var result = Person(
name = name ?: throw JsonDataException("Required property 'name' missing at ${reader.path}"))
return result
}
override fun toJson(writer: JsonWriter, value: Person?) {
if (value == null) {
throw NullPointerException("value was null! Wrap in .nullSafe() to write nullable values.")
}
writer.beginObject()
writer.name("name")
stringAdapter.toJson(writer, value.name)
writer.endObject()
}
}
因此,当您为 Person
请求适配器时,Moshi 将使用生成的 PersonJsonAdapter
而不是 ClassJsonAdapter
。
奖金
Moshi uses reflection to instantiate the generated adapter(然后被缓存,以便下次您为相同的 class 请求适配器时重新使用它),这样您就不需要添加任何额外的如果您想使用代码生成功能,请完全编写代码。
这是三个问题
- 代码生成为什么有用
Code gen 可用作反射式 moshi-kotlin 的编译时替代方案。它们都很有用,因为它们本身就理解 Kotlin 代码及其语言特性。没有它们,Moshi 将无法理解 Kotlin 的可空性、默认值等等。在某些情况下,Moshi 仅与标准 Java 反射巧合,您上面的示例就是其中之一。但是,这非常容易出错,在 Moshi 1.9 中,这些将被拒绝并需要生成的适配器或 kotlin-reflect。
- 它是如何工作的
Code gen 是一个注释处理器,它查找 class 用 @JsonClass(generateAdapter = true)
注释的代码。它为每个带注释的 class 生成一个优化的流适配器。这些适配器本身就是 Kotlin,因此能够利用支持目标 class 的 Kotlin 语言功能。在运行时,Moshi 以非常简单的已知名称后缀反射式地查找生成的适配器,这允许这些适配器无需手动注册适配器即可正常工作。
您可以在我的博客 post 中找到有关 1 和 2 的更多信息,关于它的发布时间:https://www.zacsweers.dev/exploring-moshis-kotlin-code-gen/
- 什么时候使用
每当您尝试在没有自己的自定义适配器的情况下使用 Moshi 序列化 Kotlin class 时,您都应该使用 moshi-kotlin 或代码生成。反射不会有构建时间开销,但在运行时要慢得多,同时还会由于 kotlin-reflect 而产生大量二进制大小成本,并且不能安全地进行混淆。代码生成会产生构建时间成本,但在运行时速度极快,二进制大小成本极低,而且大多数情况下都是混淆安全的。哪一个更适合您的用例取决于您!您还可以组合使用,例如调试构建中的反射和仅用于发布构建的代码生成。
我最近开始在我的 Android 应用程序中使用 Moshi,我很想知道更多关于注释 @JsonClass(generateAdapter = true)
really 的作用。
示例数据class:
data class Person(
val name: String
)
我能够 serialise/de-serialise 这个 class 如下:
val moshi: Moshi = Moshi.Builder().build()
moshi.adapter(Person::class.java).toJson(Person())
我没有在这里使用@JsonClass注解,因此codegen不会启动。
我的问题是,为什么以及什么时候需要使用 @JsonClass(generateAdapter = true)
早期版本的 Moshi 不支持 "codegen",因此它们完全依赖反射(即在运行时内省 classes 的能力)。但是,对于需要非常高性能的应用程序来说,这可能是个问题,因此他们添加了 "codegen" 功能来利用注释处理。基本上这允许在编译时生成代码,因此他们可以在不使用反射的情况下执行 serialisation/deserialisation。
为了启用代码生成功能,您还需要启用 Kapt,这是 Kotlin 注释处理器,否则将不会进行任何注释处理。
Here you'll find how to enable and configure Kapt, and here 您将了解如何设置 Moshi codegen 依赖项。
编辑
在您添加到问题的代码片段中,Moshi 使用 ClassJsonAdapter 序列化您的对象。该适配器利用反射来查找所有字段并创建 JSON 字符串(您可以看到 class 从 java.lang.reflect
导入内容)。
另一方面,Moshi code gen 为您的 class 生成一个 JsonAdapter。例如,让 Moshi 为您的 Person
class 创建适配器将生成以下内容:
// Code generated by moshi-kotlin-codegen. Do not edit.
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonDataException
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.Moshi
import java.lang.NullPointerException
import kotlin.String
class PersonJsonAdapter(moshi: Moshi) : JsonAdapter<Person>() {
private val options: JsonReader.Options = JsonReader.Options.of("name")
private val stringAdapter: JsonAdapter<String> =
moshi.adapter<String>(String::class.java, kotlin.collections.emptySet(), "name")
override fun toString(): String = "GeneratedJsonAdapter(Person)"
override fun fromJson(reader: JsonReader): Person {
var name: String? = null
reader.beginObject()
while (reader.hasNext()) {
when (reader.selectName(options)) {
0 -> name = stringAdapter.fromJson(reader) ?: throw JsonDataException("Non-null value 'name' was null at ${reader.path}")
-1 -> {
// Unknown name, skip it.
reader.skipName()
reader.skipValue()
}
}
}
reader.endObject()
var result = Person(
name = name ?: throw JsonDataException("Required property 'name' missing at ${reader.path}"))
return result
}
override fun toJson(writer: JsonWriter, value: Person?) {
if (value == null) {
throw NullPointerException("value was null! Wrap in .nullSafe() to write nullable values.")
}
writer.beginObject()
writer.name("name")
stringAdapter.toJson(writer, value.name)
writer.endObject()
}
}
因此,当您为 Person
请求适配器时,Moshi 将使用生成的 PersonJsonAdapter
而不是 ClassJsonAdapter
。
奖金
Moshi uses reflection to instantiate the generated adapter(然后被缓存,以便下次您为相同的 class 请求适配器时重新使用它),这样您就不需要添加任何额外的如果您想使用代码生成功能,请完全编写代码。
这是三个问题
- 代码生成为什么有用
Code gen 可用作反射式 moshi-kotlin 的编译时替代方案。它们都很有用,因为它们本身就理解 Kotlin 代码及其语言特性。没有它们,Moshi 将无法理解 Kotlin 的可空性、默认值等等。在某些情况下,Moshi 仅与标准 Java 反射巧合,您上面的示例就是其中之一。但是,这非常容易出错,在 Moshi 1.9 中,这些将被拒绝并需要生成的适配器或 kotlin-reflect。
- 它是如何工作的
Code gen 是一个注释处理器,它查找 class 用 @JsonClass(generateAdapter = true)
注释的代码。它为每个带注释的 class 生成一个优化的流适配器。这些适配器本身就是 Kotlin,因此能够利用支持目标 class 的 Kotlin 语言功能。在运行时,Moshi 以非常简单的已知名称后缀反射式地查找生成的适配器,这允许这些适配器无需手动注册适配器即可正常工作。
您可以在我的博客 post 中找到有关 1 和 2 的更多信息,关于它的发布时间:https://www.zacsweers.dev/exploring-moshis-kotlin-code-gen/
- 什么时候使用
每当您尝试在没有自己的自定义适配器的情况下使用 Moshi 序列化 Kotlin class 时,您都应该使用 moshi-kotlin 或代码生成。反射不会有构建时间开销,但在运行时要慢得多,同时还会由于 kotlin-reflect 而产生大量二进制大小成本,并且不能安全地进行混淆。代码生成会产生构建时间成本,但在运行时速度极快,二进制大小成本极低,而且大多数情况下都是混淆安全的。哪一个更适合您的用例取决于您!您还可以组合使用,例如调试构建中的反射和仅用于发布构建的代码生成。