有一个带有参数化构造函数的简单类。我正在寻找一种将其应用于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
以获得字节码中的参数名称。如果名称在字节码中不可用,则不能通过反射获得;你会得到arg0
、arg1
等
一些框架已经更新为使用参数名称,但很多还没有。据我所知,Jackson属于第二组(很可能仍然支持旧的Java版本(。您可以尝试使用-parameters
标志,但可能仍然需要告诉Jackson使用构造函数——要么在构造函数参数上使用@JsonProperty
,要么使用@JsonCreator
。