基于Moshi https://github.com/square/moshi/issues/843中的这个问题,对于Moshi为什么以及如何对待null的方式进行了很好的讨论。尽管这对我来说很有意义,但不幸的是,我正在使用间歇性返回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经验的人会说这样做完全可怕,或者这可能是我为自己的束缚所能得到的最好的选择?
编辑:
尽管我对这是否是一种好方法没有任何答案,但我可以确认这带来了两个隐藏的问题,并且我不建议您采用这种方法。
在json中长的数字,例如1583674200000
,如果您已将其转换为Java String类型,则该数字将不起作用。您会认为自己会得到“ 1583674200000”,但实际上却得到“ 1.5836742E12”
当您具有字符串编号时,它将转换为双精度。因此,您可能有一个“ 100”,但它将转换为100.0。
这似乎是因为moshi中的数字是双精度的,并且根本原因是作为Map的readJsonValue(),因为它首先必须将所有值映射到moshi可以理解的默认值中,然后才能过滤出空值。这是我遇到问题的原因。
TLDR不要使用此工厂。
编辑2: 我已收到通知,我在此处的编辑错误。我不确定,但是我们在这里https://github.com/square/moshi/issues/843
进行讨论