如何让Jackson在没有显式JsonProperty的情况下将构造函数字段名识别为属性



有一个带有参数化构造函数的简单类。我正在寻找一种将其应用于JSON反序列化的方法,同时使用最少的注释。

如何使Jackson在没有显式JsonProperty的情况下通过名称识别id字段?

类别:

public final class MyRec {
final String id;
public MyRec1(String id) {
this.id = id;
}
}

除非字段上存在注释@JsonProperty("id"),否则反序列化将失败。

反序列化尝试:

new ObjectMapper().readValue("""{"id":"abc"}""", MyRec.class);

异常:

Cannot construct instance of `sample.MyRec` (although at least one Creator exists): 
cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"id":"abc"}"; line: 1, column: 2]
com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot construct instance of `sample.MyRec` (although at least one Creator exists):
cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"id":"abc"}"; line: 1, column: 2]
at app//com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)

参数名称模块

要使Jackson能够在不使用数据绑定注释@JsonCreator@JsonProperty的情况下根据属性的数量检测所需的构造函数,您可以向Jackson Modules:Java 8添加依赖项(Maven存储库的链接(。

Jackson模块:Java 8是一个包含参数名称模块的伞形多模块。

如果您使用的是Spring Boot,那么注册模块所需的唯一一件事就是将ParameterNamesModule作为Bean放入Spring的Context中,并且它将在应用程序启动时被JacksonAutoConfiguration捕获并自动注册,而ObjectMapper将被配置。

正如这里所解释的,在实例化模块时,您需要提供JsonCreator.Mode.PROPERTIES作为参数。

@Bean
public ParameterNamesModule parameterNamesModule() {
return new ParameterNamesModule(JsonCreator.Mode.PROPERTIES);
}

如果您的项目中没有使用Spring,那么您需要手动配置ObjectMapper

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));

备注

存在注意事项。特别是在检测单个arg构造函数时存在问题。以下是模块描述中的一句话:

前提条件:

  • 类Person必须使用符合Java 8的编译器进行编译,该编译器打开了存储正式参数名称的选项(-parameters选项(。有关访问参数名称的Java 8 API的更多信息,请访问运行时查看此
  • 如果有多个可见的构造函数,并且没有默认的构造函数,则需要@JsonCreator为反序列化
  • 如果与低于2.6.0的jackson数据绑定一起使用,则需要@JsonCreator。在实践中,2.6.0也经常需要@JsonCreator+由于构造函数发现的问题将在2.7中解决
  • 如果类Person具有单参数构造函数,则其参数需要用@JsonProperty("propertyName")进行注释。这是为了保留遗留行为,请参阅FasterXML/jackson databind/#1498

因此,为了使Jackson识别单个arg构造函数,我们需要在参数上应用@JsonProperty

这里还提到了这个问题JsonCreator.Mode.PROPERTIES文档:

模式,指示创建者的参数将从传入Object值的匹配属性绑定,使用创建者参数名称(显式或隐式(将传入Object属性与参数匹配。

注意此模式当前(2.5(始终用于多重论证创造者;唯一不明确的情况是单参数创建者


此行为可能会在此处提到的Jackson 3中得到修复

Java 16记录

如果您在项目中使用JDK16+,那么您可以将最后一个类作为记录。

记录没有声明默认的无参数构造函数,相反,编译器生成一个所谓的规范构造函数,声明记录头中的所有参数(请参阅JLS 8.10.4。记录构造函数声明以获取更多信息(。

Jackson使用规范构造函数作为默认创建者来实例化记录。

以下记录将被取消序列化而不会出现问题:

public record MyRec(String id) {}

在Java 8之前,方法和构造函数参数的名称不存在于编译的字节码中。在Java8及更高版本中,需要显式设置-parameters以获得字节码中的参数名称。如果名称在字节码中不可用,则不能通过反射获得;你会得到arg0arg1

一些框架已经更新为使用参数名称,但很多还没有。据我所知,Jackson属于第二组(很可能仍然支持旧的Java版本(。您可以尝试使用-parameters标志,但可能仍然需要告诉Jackson使用构造函数——要么在构造函数参数上使用@JsonProperty,要么使用@JsonCreator

最新更新