kotlin - Moshi 工厂在从 json 字符串反序列化时忽略空值并使用 kotlin 默认值
问题描述
基于 Moshi https://github.com/square/moshi/issues/843中的这个问题,关于 Moshi 为何以及如何以这种方式处理空值进行了一些很好的讨论。虽然这对我来说很有意义,但不幸的是,我正在使用一个间歇性返回 null 的 api,而我得到的最好的反馈是我们应该忽略它们。在上面链接的问题中,图书馆贡献者之一提到了这个可以提供帮助的工厂。
@Retention(RUNTIME)
@Target(CLASS)
annotation class DefaultIfNull
class DefaultIfNullFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>,
moshi: Moshi): JsonAdapter<*>? {
if (!Types.getRawType(type).isAnnotationPresent(
DefaultIfNull::class.java)) {
return null
}
val delegate = moshi.nextAdapter<Any>(this, type, annotations)
return object : JsonAdapter<Any>() {
override fun fromJson(reader: JsonReader): Any? {
@Suppress("UNCHECKED_CAST")
val blob = reader.readJsonValue() as Map<String, Any?>
val noNulls = blob.filterValues { it != null }
return delegate.fromJsonValue(noNulls)
}
override fun toJson(writer: JsonWriter, value: Any?) {
return delegate.toJson(writer, value)
}
}
}
}
class NullSkipperTest {
@DefaultIfNull
data class ClassWithDefaults(val foo: String, val bar: String? = "defaultBar")
@Test
fun skipNulls() {
//language=JSON
val json = """{"foo": "fooValue", "bar": null}"""
val adapter = Moshi.Builder()
.add(DefaultIfNullFactory())
.add(KotlinJsonAdapterFactory())
.build()
.adapter(ClassWithDefaults::class.java)
val instance = adapter.fromJson(json)!!
check(instance.bar == "defaultBar")
}
}
这很好用,但对于我的用例来说并不是“最佳”的,因为有两个单独的问题:1. 它需要注释每个类。我有大约 200 个需要注释的类,所以我正在寻找一个可以做而不是选择加入的工厂。如果有的话,也许我想选择退出,但这并不重要现在 2. 从也发布的测试中,不清楚我的所有值是否都需要声明为可为空的。我还希望不必将每个字段都标记为可为空的类型,但测试似乎通过了val foo: String, val bar: String = "defaultBar"
而不是val foo: String, val bar: String? = "defaultBar"
为了让这个为我自己工作,我将片段转换为这个。它似乎也解决了我上面提到的两个“问题”。
class DefaultIfNullFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>,
moshi: Moshi): JsonAdapter<*>? {
val delegate = moshi.nextAdapter<Any>(this, type, annotations)
return object : JsonAdapter<Any>() {
override fun fromJson(reader: JsonReader): Any? {
val blob1 = reader.readJsonValue()
try {
val blob = blob1 as Map<String, Any?>
val noNulls = blob.filterValues { it != null }
return delegate.fromJsonValue(noNulls)
} catch (e: Exception) {
return delegate.fromJsonValue(blob1)
}
}
override fun toJson(writer: JsonWriter, value: Any?) {
return delegate.toJson(writer, value)
}
}
}
}
我不知道工厂是它在一个班级的每个领域以及班级中移动,所以当我最初拥有这个时,我认为它会很完美,但它确实在每个领域都被调用在每个类中,强制转换失败,然后只返回值。
我对它感到满意,而且我没有感觉到任何性能影响。有更多moshi经验的人会说这样做非常糟糕,或者这可能是我能得到的最好的限制?
编辑:
虽然我没有关于这是否是一个好方法的答案,但我可以确认这带来了两个隐藏的问题,我不建议你走这条路。
如果您将它们调整为转换为 java String 类型,那么 json 中的 long 数字
1583674200000
将不起作用。你会认为你会得到“1583674200000”,但实际上你会得到“1.5836742E12”当您有一个字符串数字时,它将被转换为双精度数。所以你可能有一个“100”,但它会被转换为 100.0。
这似乎是因为 moshi 中的数字是双倍的,根本原因是 readJsonValue() as Map,因为它首先必须将所有值映射到 moshi 可以理解的默认值,然后才能过滤掉空值。这是我的问题的原因。
TLDR 不要使用这个工厂。
编辑 2: 我被告知我的编辑在这里是错误的。我不确定是不是,但我们在这里讨论它https://github.com/square/moshi/issues/843
解决方案
推荐阅读
- node.js - 类型错误:device.devices.map 不是函数
- excel - 将文件发送到其他计算机时自动添加一行
- ruby - 在 ruby 中,我们如何将两个给定的哈希合并为单个哈希,并用两个值中的较大值替换重复的键值
- powerquery - Power Query - 数据转换
- html - 从引导表中检索函数中的所有计数器值
- rust - Rust 是否允许您为您的类型和外部类型定义 Ord 或 PartialOrd?
- flutter - 在 Flutter Web 应用程序中实现橙色货币支付网关时出错
- c - 循环中 fread 后检查 EOF
- chilkat - Chilkat.CkString 缺失
- python - PyQt5 Mac拖拽窗口事件类型与收缩事件类型相同