FasterXML / jackson-module-kotlin

Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes.
Apache License 2.0
1.12k stars 175 forks source link

How to use default value when deserializing Json object with null value #717

Closed GaoHGit closed 11 months ago

GaoHGit commented 12 months ago

Your question

When using propertyNamingStrategy and KotlinFeature.NullIsSameAsDefault together, null is not successfully set as default value.

Tried versio jackson-module-kotlin 2.15.2 and 2.11.4 both jackson-core-2.11.4

This is my test cases with jackson-module-kotlin 2.15.2

  class TestObj {
    val dataList: List<String> = emptyList()
  }

  @Test
  fun mapperTestWith() {
    val mapper = ObjectMapper().apply {
      registerModules(
        KotlinModule.Builder()
          .configure(KotlinFeature.NullIsSameAsDefault, enabled = true)
          .build()
      )
      //Simple implementation Json with PascalCase
      propertyNamingStrategy = object : PropertyNamingStrategy() {
        private val serialVersionUID = 1L
        override fun nameForSetterMethod(
          config: MapperConfig<*>?,
          method: AnnotatedMethod,
          defaultName: String
        ): String {
          return method.name.substring(3)
        }

        override fun nameForGetterMethod(
          config: MapperConfig<*>?,
          method: AnnotatedMethod,
          defaultName: String
        ): String {
          return if (method.name.startsWith("is")) method.name.substring(2) else method.name.substring(3)
        }
      }
    }
    val json = """
      {"DataList": null}
      """.trimIndent()
    val data = mapper.readValue(json, TestObj::class.java)
    /*
    com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "DataList" (MyTestClass), not marked as ignorable (one known property: "dataList"])
 at [Source: (String)"{"DataList": null}"; line: 1, column: 18] (through reference chain: MyTestClass$TestObj["DataList"])
     */
    println(data)
  }

I don't know if the logic of my propertyNamingStrategy is incorrect causing this exception, Is this a bug or something wrong with the way I use it?

GaoHGit commented 12 months ago

I re-tried a simple use case in version 2.12.7, but an exception still occurred. It seems that the parameters I set did not work as I thought: when the value of Json is null, use the default value to set it to the attribute.

  class TestObj {
    var dataList: List<String> = emptyList()
  }

  @Test
  fun mapperNullTest() {
    val mapper = ObjectMapper().apply {
      registerModules(
        KotlinModule(nullIsSameAsDefault = true)
      )
    }
    val json = """
      {"dataList": null}
      """.trimIndent()
    val data = mapper.readValue(json, TestObj::class.java)
    println(data)
  }

excption com.fasterxml.jackson.databind.JsonMappingException: Parameter specified as non-null is null: method MyTestClass$TestObj.setDataList, parameter <set-?> at [Source: (String)"{"dataList": null}"; line: 1, column: 14] (through reference chain: MyTestClass$TestObj["dataList"])

at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:281)
k163377 commented 12 months ago

NullIsSameAsDefault works only when deserializing with a constructor or factory function. Therefore, the case you submitted is not applicable.

I have not been able to check much, but from looking at 2.16, it seemed that at least the following would prevent the error from occurring.

import com.fasterxml.jackson.annotation.JsonSetter
import com.fasterxml.jackson.annotation.Nulls

class TestObj {
    @JsonSetter(nulls = Nulls.SKIP)
    var dataList: List<String> = emptyList()
}

Personally, I recommend deserialization via the constructor.

cowtowncoder commented 12 months ago

Version 2.16.0-rc1 was just released, for what that's worth; may be worth verifying.

GaoHGit commented 11 months ago

NullIsSameAsDefault仅在使用构造函数或工厂函数反序列化时有效。因此,您提交的案例不适用。

我无法检查太多,但是从查看2.16来看,似乎至少以下内容可以防止错误发生。

import com.fasterxml.jackson.annotation.JsonSetter
import com.fasterxml.jackson.annotation.Nulls

class TestObj {
    @JsonSetter(nulls = Nulls.SKIP)
    var dataList: List<String> = emptyList()
}

就个人而言,我建议通过构造函数进行反序列化。

It seems that I misunderstood the role of this parameter. It seems that I should also try to set the properties that require default values to the constructor.