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

Moshi工厂在从json字符串反序列化时忽略空值并使用kotlin默认值,kotlin,moshi,Kotlin,Moshi,基于Moshi的这一问题,对Moshi为什么以及如何以这种方式处理空值进行了一些很好的讨论。虽然这对我来说很有意义,但不幸的是,我正在使用一个间歇返回空值的api,我得到的最好反馈是我们应该忽略它们。在上面链接的问题中,一个库贡献者提到这个工厂可能会有所帮助 @Retention(RUNTIME) @Target(CLASS) annotation class DefaultIfNull class DefaultIfNullFactory : JsonAdapter.Factory {

基于Moshi的这一问题,对Moshi为什么以及如何以这种方式处理空值进行了一些很好的讨论。虽然这对我来说很有意义,但不幸的是,我正在使用一个间歇返回空值的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")
  }
}
我所不知道的是,工厂会在类中的每个字段上移动,也会在类中移动,所以当我最初使用工厂时,我认为它是完美的,但它确实会在每个类中的每个字段上被调用,转换失败,然后只返回值

我现在对它感到满意,我没有感觉到任何性能上的问题。有更多摩希经验的人会说这样做太可怕了,还是说这可能是我所能得到的最好的限制

编辑:

虽然我还没有关于这是否是一个好的方法的答案,但我可以确认这带来了两个隐藏的问题,我可以建议你走这条路

  • 如果您将json中的长字符(如
    158367420000
    )转换为java字符串类型,则这些数字将不起作用。你可能以为你会得到“158364200000”,但实际上你得到的是“1.5836742E12”

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

  • 这似乎是因为moshi中的数字是双倍的,根本原因是readJsonValue()作为映射,因为它首先必须将所有值映射到moshi可以理解的默认值,然后过滤掉空值。这就是我的问题的原因

    TLDR不使用此工厂

    编辑2:
    我被告知我在这里的编辑是错误的。我不确定它是否正确,但我们在这里讨论它

    我复制并修改了
    KotlinJsonAdapter
    ,并从中删除了空值,并使用了默认值。它适用于您的TLDR不要使用此工厂案例@coltonidle@UmeshChhabra你能分享一个要点吗?如果有人想检查和评论副作用、bug,我已经做了一些修改etc@UmeshChhabra我还没有试过你的代码,但我快速看了一下。看起来真不错!也感谢所有这些测试!这肯定会在将来帮助很多人!试着检查一下:我已经复制并修改了
    KotlinJsonAdapter
    ,删除了其中的空值,并使用了默认值。它适用于您的TLDR不要使用此工厂案例@coltonidle@UmeshChhabra你能分享一个要点吗?如果有人想检查和评论副作用、bug,我已经做了一些修改etc@UmeshChhabra我还没有试过你的代码,但我快速看了一下。看起来真不错!也感谢所有这些测试!这肯定会在将来帮助很多人!请尝试检查以下内容:
    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)
                }
            }
        }
    }