【问题标题】:Why does Jackson throw an `UnrecognizedPropertyException` for Kotlin boolean-typed `val`s starting with `is`?为什么杰克逊会为 Kotlin 布尔类型的 `val` 以`is`开头抛出`UnrecognizedPropertyException`?
【发布时间】:2019-12-03 18:52:57
【问题描述】:
class JacksonError(
    val x: String,
    val isSomething: Boolean
)

因错误而失败

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "something" (class ch.cypherk.blah.JacksonError), not marked as ignorable (2 known properties: "x", "isSomething"])

而没有这样的问题

class NoJacksonError(
    val x: String,
    val something: Boolean
)

我的第一直觉是 Jackson 不理解 isXY-getters,它只是 需要 getters 以 get 开头。

有趣的是,

class AlsoNoJacksonError (
    val x: String,
    var isSomething: Boolean
)

这意味着我不知道发生了什么。

JacksonError 被编译为

public final class ch.cypherk.blah.JacksonError {
  public final java.lang.String getX();
    Code:
       0: aload_0
       1: getfield      #11                 // Field x:Ljava/lang/String;
       4: areturn

  public final boolean isSomething();
    Code:
       0: aload_0
       1: getfield      #18                 // Field isSomething:Z
       4: ireturn

  public ch.cypherk.blah.JacksonError(java.lang.String, boolean);
    Code:
       0: aload_1
       1: ldc           #21                 // String x
       3: invokestatic  #27                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokespecial #30                 // Method java/lang/Object."<init>":()V
      10: aload_0
      11: aload_1
      12: putfield      #11                 // Field x:Ljava/lang/String;
      15: aload_0
      16: iload_2
      17: putfield      #18                 // Field isSomething:Z
      20: return
}

NoJacksonError 被编译为

public final class ch.cypherk.blah.NoJacksonError {
  public final java.lang.String getX();
    Code:
       0: aload_0
       1: getfield      #11                 // Field x:Ljava/lang/String;
       4: areturn

  public final boolean getSomething();
    Code:
       0: aload_0
       1: getfield      #19                 // Field something:Z
       4: ireturn

  public ch.cypherk.blah.NoJacksonError(java.lang.String, boolean);
    Code:
       0: aload_1
       1: ldc           #22                 // String x
       3: invokestatic  #28                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokespecial #31                 // Method java/lang/Object."<init>":()V
      10: aload_0
      11: aload_1
      12: putfield      #11                 // Field x:Ljava/lang/String;
      15: aload_0
      16: iload_2
      17: putfield      #19                 // Field something:Z
      20: return
}

到目前为止,这符合我的期望; JacksonError 有一个 isSomething() 吸气剂,而NoJacksonError 有一个 getSomething() 吸气剂。

但随后AlsoNoJacksonError 被编译为

public final class ch.cypherk.blah.AlsoNoJacksonError {
  public final java.lang.String getX();
    Code:
       0: aload_0
       1: getfield      #11                 // Field x:Ljava/lang/String;
       4: areturn

  public final boolean isSomething();
    Code:
       0: aload_0
       1: getfield      #18                 // Field isSomething:Z
       4: ireturn

  public final void setSomething(boolean);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #18                 // Field isSomething:Z
       5: return

  public ch.cypherk.blah.AlsoNoJacksonError(java.lang.String, boolean);
    Code:
       0: aload_1
       1: ldc           #24                 // String x
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokespecial #33                 // Method java/lang/Object."<init>":()V
      10: aload_0
      11: aload_1
      12: putfield      #11                 // Field x:Ljava/lang/String;
      15: aload_0
      16: iload_2
      17: putfield      #18                 // Field isSomething:Z
      20: return
}

其中有一个isSomething() getter...

那么为什么JacksonError 会产生异常呢?

以及如何让杰克逊正确识别该属性?

【问题讨论】:

标签: java kotlin jackson jackson-databind


【解决方案1】:

解决问题有两个因素。
第一:
如果没有指定 @JsonProperty 字段,Jackson 默认使用 Java bean 命名约定来暗示 json 字段名称 (For a boolean field, what is the naming convention for its getter/setter?)。 我测试了代码,并且序列化适用于 JacksonError 类,因为有一个符合 bean 访问器的 Java 命名标准的 getter。然而,在反序列化时,它看起来是通过名称“setSomething()”访问一个 getter。 如果您观察到具有 setSomething 方法的类,则不会给出反序列化错误。

第二:没有为您的 JacksonError 类生成 setSomething() 方法的原因是它是一个 val 字段并且它不会生成一个 setter(val 字段是不可变的,并且仅在构造函数中初始化,因此没有 setter)。就像@Erwin Bolwidt 建议的那样,您可以将以下属性设置为对象映射器以包含杰克逊的 kotlin 模块

 val mapper = ObjectMapper().registerKotlinModule() 
        .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
        .setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE)

ObjectMapper().registerKotlinModule :注册有助于对 kotlin 类进行序列化/反序列化的模块。 Git 链接 (https://github.com/FasterXML/jackson-module-kotlin)。

setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) :使所有字段可序列化,包括私有字段(在您的情况下为 val 字段)

        .setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.NONE)  :  Makes sure any static / factory constructors are not autodetected

        .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE) :  

所有这些道具都通知对象映射器忽略任何 getter/setter 和 boolean setter 的自动检测

有关更多信息,请参阅以下主题: Usage of Jackson @JsonProperty annotation for kotlin data classes

【讨论】:

  • 您链接到的主题实际上并不包含“更多信息”。按照您建议的方式设置可见性实际上有什么作用?
  • @User1291 我已将更改添加到我的 asnwer 中,指定每个可见性修饰符的作用。
猜你喜欢
  • 2018-09-24
  • 1970-01-01
  • 2019-07-20
  • 2019-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-07
相关资源
最近更新 更多