从json字符串反序列化时,Moshi工厂将忽略空值并使用kotlin默认值

时间:2020-02-24 14:14:48

标签: kotlin moshi

基于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经验的人会说这样做完全可怕,或者这可能是我为自己的束缚所能得到的最好的选择?

编辑:

尽管我对这是否是一种好方法没有任何答案,但我可以确认这带来了两个隐藏的问题,并且我建议您采用这种方法。

  1. 在json中长的数字,例如1583674200000,如果您已将其转换为Java String类型,则该数字将不起作用。您会认为自己会得到“ 1583674200000”,但实际上却得到“ 1.5836742E12”

  2. 当您具有字符串编号时,它将转换为双精度。因此,您可能有一个“ 100”,但它将转换为100.0。

这似乎是因为moshi中的数字是双精度的,并且根本原因是作为Map的readJsonValue(),因为它首先必须将所有值映射到moshi可以理解的默认值中,然后才能过滤出空值。这是我遇到问题的原因。

TLDR不要使用此工厂。

编辑2: 我已收到通知,我在此处的编辑错误。我不确定,但是我们在这里https://github.com/square/moshi/issues/843

进行讨论

0 个答案:

没有答案