升级到Spring Boot 2后,ObjectMapper无法在没有默认构造函数的情况下反序列化

sol*_*nmv 28 java spring jackson lombok spring-boot

我有以下DTO:

@Value
public class PracticeResults {
    @NotNull
    Map<Long, Boolean> wordAnswers;
}

@Value
public class ProfileMetaDto {

    @NotEmpty
    String name;
    @Email
    String email;
    @Size(min = 5)
    String password;
}
Run Code Online (Sandbox Code Playgroud)

@Value是一个生成构造函数的Lombok注释.这意味着该类没有no-arg构造函数.

我使用Spring Boot 1.4.3.RELEASE和ObjectMapper bean能够从json反序列化这样的对象.

升级到Spring Boot 2.0.0.M7后,我收到以下异常:

@Value

Spring Boot 1.4.3中使用的Jackson版本适用于ObjectMapperSpring Boot 2.0.0.M7是com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of PracticeResults (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator).

我试过谷歌这个问题,但发现只有2.8.10或的解决方案2.9.2.

那么,为什么它与Spring Boot 1.4.3一起使用并且在Spring Boot 2中失败?是否可以将bean配置为与旧版本相同的方式?

Tob*_*ias 31

由于Lombok 1.16.20版本中的更改,您需要在lombok.config文件中设置以下属性(如果您没有此文件,则可以在项目根目录中创建它):

lombok.anyConstructor.addConstructorProperties=true
Run Code Online (Sandbox Code Playgroud)

这在Lombok changelog中描述:https://projectlombok.org/changelog.

之后,杰克逊应再次接受@Value.

您可能有兴趣在此处关注相关的GitHub问题:https://github.com/rzwitserloot/lombok/issues/1563


lee*_*der 7

我遇到了这个问题以及对我有用的解决方案,即创建没有字段默认构造函数,问题就消失了。

  • 如果我想创建非可变对象,这就会成为一个问题 (6认同)

mar*_*ste 7

如果您的类中有一些最终字段,并且您希望对象创建者使用特定的构造函数来反序列化您的 POJO 以及@Data注释,我建议@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)使用@JsonProperty("<field_name>"). 该示例如下所示:

package test.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.util.Date;

@Data
public class KafkaEventPojo {
    private final String executionId;
    private final String folder;
    private final Date created_at;

    private Date updated_at;
    // ... other params
    
    /**
     * Instantiates a new Kafka event pojo.
     *
     * @param executionId the execution or flow id of the event lifecycle
     * @param folder      the folder to be retrieved
     */
    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public KafkaEventPojo(@JsonProperty("executionId") String executionId, @JsonProperty("folder") String folder) {
        this(executionId, folder, new Date(System.currentTimeMillis()));
    }

    private KafkaEventPojo(String executionId, String folder, Date date) {
        this.executionId = executionId;
        this.folder = folder;

        this.created_at = date;
        this.updated_at = date;
    }
    
    // ... other logic
}
Run Code Online (Sandbox Code Playgroud)


sol*_*nmv 6

解决此问题的另一种方法。使用Jackson 参数名称模块,默认情况下包含在spring boot 2中。之后,Jackson可以反序列化对象。但是,仅当对象中具有多个属性时,它才有效。在单个属性的情况下,我收到以下错误消息:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `SomeClassName` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Run Code Online (Sandbox Code Playgroud)

由于以下原因:

标记批注,可用于将构造函数和工厂方法定义为用于实例化关联类的新实例的标记。

注意:注释创建者方法(构造函数,工厂方法)时,方法必须为:

  • 单参数构造函数/工厂方法,不带JsonProperty参数注释:如果是这样,这就是所谓的“委托创建者”,在这种情况下,Jackson首先将JSON绑定到参数类型中,然后调用creator。这通常与JsonValue(用于序列化)结合使用。
  • 构造函数/工厂方法,其中每个参数都用JsonProperty或注释JacksonInject,以指示要绑定的属性的名称

另请注意,JsonProperty除非您使用可以检测参数名称的扩展模块之一,否则所有注释都必须指定实际名称(“ default”不得为空字符串)。这是因为8之前的默认JDK版本无法存储和/或从字节码中检索参数名称。但是对于JDK 8(或使用诸如Paranamer之类的帮助程序库,或诸如Scala或Kotlin之类的其他JVM语言),指定名称是可选的。

为了使用Lombok处理这种​​情况,我使用了以下解决方法:

@Value
@AllArgsConstructor(onConstructor = @__(@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)))
class SomeClassName {...}
Run Code Online (Sandbox Code Playgroud)