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

如何让杰克逊在没有明确JsonProperty的情况下,通过名字来识别id场?

Class:

public final class MyRec {

    final String id;

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

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

Deserialization attempt:

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

Exception:

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)

推荐答案

Parameter names module

为了使Jackson能够利用数据绑定注释@JsonCreator@JsonProperty基于属性without的数量检测所需的构造函数,您可以向Jackson Modules: Java 8(a link to the Maven repository)添加依赖项.

Jackson模块:Java 8是一个伞形的多模块,包括Parameter names module个模块.

如果您使用的是Spring Boot,注册模块唯一需要做的就是将ParameterNamesModule作为Bean放入Spring的上下文中,它将在应用程序启动时被抓取并由JacksonAutoConfiguration自动注册,而ObjectMapper将被配置.

正如所解释的here,在实例化模块时,您需要提供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));

注意事项

一共有caveats个.具体地说,检测single-arg constructor有一个问题.以下是module-description强中的一句话:

Preconditions:

  • 类Person必须使用Java 8兼容的编译器进行编译,并打开存储形参名称的选项(-PARAMETERS选项). 有关访问参数名称的Java 8 API的更多信息,请访问 运行时请查看以下内容
  • 如果有多个可见构造函数,但没有默认构造函数,则需要@JsonCreator为 反序列化
  • 如果与低于2.6.0的Jackson-data ind配合使用,则@JsonCreator为必填项.实际上,2.6.0+也经常需要@JsonCreator 由于构造函数发现的问题,将在2.7中解决.
  • 如果类Personsingle argument constructor,则其参数needs to be annotated@JsonProperty("propertyName").这是为了 preserve legacy behavior,参见FasterXML/jackson-data ind/#1498

因此,为了让杰克逊识别出single-arg constructor分,我们需要在参数上应用@JsonProperty分.

这里还提到了这一JsonCreator.Mode.PROPERTIES期文档:

一种模式,指示创建者的参数将从传入对象值的匹配属性中绑定,使用创建者参数名称(显式或隐式)将传入对象属性与参数相匹配.

此模式当前(2.5)始终用于的注意事项 多论元创建者;only ambiguous case is that of a single-argument creator.


这一行为是might be fixed,杰克逊3是mentioned here.

Java 16 Records

如果您在项目中使用JDK 16+,那么您可以使最后一个类成为记录.

记录doesn't声明默认的无参数构造器,相反,编译器生成所谓的canonical constructor,声明来自记录头的所有参数(refer to the JLS 100 for more information).

杰克逊使用canonical constructor作为默认创建者来实例化记录.

以下记录将不会出现问题:

public record MyRec(String id) {}

Java相关问答推荐

如果一个子类没有构造函数,超类也没有构造函数,那么为什么我可以构造子类的实例呢?

Java .类参数不通过构造函数传递

如何修复IndexOutOfBoundsException在ClerView适配器的onRowMoved函数?

相同的Java SerializedLambda为implMethodKind返回不同的结果

Spark忽略Iceberg Nessie目录

如何在Java记录中设置BigDecimal类型属性的精度?

如何从日期中截取时间并将其传递给组件?

Java连接池无法正常工作

使用Jackson库反序列化json

如何在透视表中添加对计数列的筛选?

记录是类的语法糖吗?

模拟JUnit未检测到返回字符串的方法的任何声纳覆盖

在单例类上获取Java锁,了解原因

如何在Spring Boot中为不同的部署环境管理多个.properties文件?

验证没有';t work on Hibernate Entity';s字段

设置背景时缺少Android编辑文本下划线

ResponseEntity.控制器截断响应的JSON部分

setMaxTotal实际上是做什么的? (MySQL与Java和Apache BasicDataSource连接池)

Hibernate在删除Entity后不会抛出EntityNotFoundException

Java 字符串输出为空白