Moshi 1.9.x 无法序列化 Kotlin 类型

Posted

技术标签:

【中文标题】Moshi 1.9.x 无法序列化 Kotlin 类型【英文标题】:Moshi 1.9.x Cannot serialize Kotlin type 【发布时间】:2020-02-28 19:40:37 【问题描述】:

升级到Moshi 1.9.1(来自1.8.0)后,我遇到了以下崩溃和堆栈跟踪:

java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.garpr.android.data.models.RankedPlayer. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact.
for class com.garpr.android.data.models.RankedPlayer
for class com.garpr.android.data.models.AbsPlayer

    at com.squareup.moshi.Moshi$LookupChain.exceptionWithLookupStack(Moshi.java:349)
    at com.squareup.moshi.Moshi.adapter(Moshi.java:150)
    at com.squareup.moshi.Moshi.adapter(Moshi.java:98)
    at com.squareup.moshi.AdapterMethodsFactory$AdapterMethod.bind(AdapterMethodsFactory.java:313)
    at com.squareup.moshi.AdapterMethodsFactory.create(AdapterMethodsFactory.java:62)
    at com.squareup.moshi.Moshi.adapter(Moshi.java:138)
    at com.squareup.moshi.Moshi.adapter(Moshi.java:98)
    at com.squareup.moshi.Moshi.adapter(Moshi.java:72)
    at com.garpr.android.data.converters.AbsPlayerConverterTest.setUp(AbsPlayerConverterTest.kt:76)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:546)
    at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:252)
    at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.garpr.android.data.models.RankedPlayer. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact.
    at com.squareup.moshi.ClassJsonAdapter$1.create(ClassJsonAdapter.java:83)
    at com.squareup.moshi.Moshi.nextAdapter(Moshi.java:169)
    at com.squareup.moshi.AdapterMethodsFactory$AdapterMethod.bind(AdapterMethodsFactory.java:312)
    at com.squareup.moshi.AdapterMethodsFactory.create(AdapterMethodsFactory.java:62)
    at com.squareup.moshi.Moshi.adapter(Moshi.java:138)
    ... 23 more

我看到了this other Stack Overflow answer,但它不适用于我,因为我已经在我的课程中添加了相关的@JsonClass(generateAdapter = X) 行。

我为我的AbsPlayer 类使用自定义AbsPlayerConverter class,这样我就可以确定要解析到哪个子类。然后,如果它解析为 RankedPlayer,我会使用另一个自定义转换器 (RankedPlayerConverter)。

这里是my Moshi-builder code:

Moshi.Builder()
        .add(AbsPlayerConverter)
        .add(AbsRegionConverter)
        .add(AbsTournamentConverter)
        .add(MatchConverter)
        .add(RankedPlayerConverter)
        .add(SimpleDateConverter)
        .build()

这是my gradle file的Moshi:

implementation "com.squareup.moshi:moshi:1.9.1"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.1"

所以最后,在所有这些文本和信息之后,我不知道我怎么会遇到这个崩溃。我已经明确定义了如何序列化/反序列化我的RankedPlayer 类。如果我降级到 Moshi 1.8.0,并让我的代码库完全保持原样,那么这个崩溃就会消失,一切都可以完美运行。

有人有什么想法吗?

谢谢!!

【问题讨论】:

那么您是否按照错误消息中的说明进行操作? Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact @ianhanniballake 好吧,我很确定我有。我认为我不需要使用KotlinJsonAdapterFactory,因为只有当您不打算使用 Moshi 的 codegen 支持时才需要它。我唯一不使用 Moshi 的代码生成器的地方是上面列出的 6 个转换器类。自 1.8.0 之前,the readme 中关于 Moshi 的反射/代码生成功能的这些信息都没有改变,当我降级到这个版本时,我的所有问题都消失了。 Kotlin part of the readme 非常清楚地表明,如果您不使用 Moshi 的代码生成器,则需要添加 KotlinJsonAdapterFactory ......就像那些特定的转换器一样。根据the blog post about Moshi 1.9,这绝对是 Moshi 1.9.X 中发生的变化。是什么让您认为这不适用于您的案例? @ianhanniballake 我只是不认为我需要 Moshi 的 Kotlin 反射功能,因为文档使它听起来很可选,例如来自 their readme 的这一行:“你可以使用反射、codegen 或两者都使用”。而且由于我以前不需要1.8.0,所以我想没有它我还是可以的……但从未见过你链接的那篇博文????。我想我现在的感觉是他们可以在自述文件中对此更加清楚。谢谢你的帮助!! @CharlesMadere 你是对的。仅仅为了 1.8 的旧文档就卡住了 2 小时 【参考方案1】:

错误信息中具体写着Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact

根据Kotlin part of the readme,如果您不使用 Moshi 的代码生成器,则必须添加KotlinJsonAdapterFactory。根据the blog post about Moshi 1.9,这是 Moshi 1.9 中的特定行为更改。

Moshi.Builder()
    .add(AbsPlayerConverter)
    .add(AbsRegionConverter)
    .add(AbsTournamentConverter)
    .add(MatchConverter)
    .add(RankedPlayerConverter)
    .add(SimpleDateConverter)
    .add(KotlinJsonAdapterFactory())
    .build()

并确保您使用的是implementation("com.squareup.moshi:moshi-kotlin:1.9.1")

【讨论】:

我不明白这部分“如果你没有使用 Moshi 的 codegen”【参考方案2】:

我正在使用改造,我必须执行以下操作:

在 build.grade 中:

implementation "com.squareup.moshi:moshi-kotlin:$moshiVersion"

在我的存储库中:

val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl(WEB_SERVICE_URL)
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .build()

【讨论】:

【参考方案3】:

当我忘记将@JsonClass(generateAdapter = true) 添加到我的一个课程时,我得到了这个。

【讨论】:

以上是关于Moshi 1.9.x 无法序列化 Kotlin 类型的主要内容,如果未能解决你的问题,请参考以下文章

Moshi 的 Kotlin 代码生成器有啥用?

(Kotlin 中的 Moshi)@Json 与 @field:Json

对kotlin友好的现代 JSON 库 moshi 基本使用和实战

对kotlin友好的现代 JSON 库 moshi 基本使用和实战

对kotlin友好的现代 JSON 库 moshi 基本使用和实战

对kotlin友好的现代 JSON 库 moshi 基本使用和实战