升级到Spring Boot 2后,ObjectMapper无需默认构造函数即可反序列化

时间:2018-01-18 21:29:27

标签: java json spring spring-boot jackson

我有以下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;
}

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

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

升级到Spring 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)

Spring Boot 1.4.3中使用的Jackson版本为2.8.10,而Spring Boot 2.0.0.M7为2.9.2

我已尝试Google解决此问题,但仅找到@JsonCreator@JsonProperty的解决方案。

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

8 个答案:

答案 0 :(得分:25)

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

lombok.anyConstructor.addConstructorProperties=true

这在Lombok更改日志中有所描述:https://projectlombok.org/changelog

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

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

答案 1 :(得分:4)

我遇到了这个问题,并且找到了一个对我有用的解决方案,创建了一个没有字段的默认构造函数,问题消失了。

答案 2 :(得分:3)

如果您的类中有一些最终字段 ,则您希望对象创建者使用特定的构造函数将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
}

答案 3 :(得分:2)

解决此问题的另一种方法。使用杰克逊parameter names module,默认情况下它包含在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)

由于以下原因:

  

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

     

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

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

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

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

@Value
@AllArgsConstructor(onConstructor = @__(@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)))
class SomeClassName {...}

答案 4 :(得分:1)

我遇到了这个问题,但是我的解决方案并未涉及添加以下内容:(mode = JsonCreator.Mode.PROPERTIES)

真正解决我问题的是构造器中的@JsonProperty

我的最终结果是:

@Value
public class ServerTimeResponse {

    long serverTime;

    @JsonCreator
    public ServerTimeResponse(@JsonProperty("serverTime") long serverTime) {
        this.serverTime = serverTime;
    }

}

答案 5 :(得分:0)

我已将lombok版本升级到:'org.projectlombok:lombok:1.18.0',它对我有用。

答案 6 :(得分:0)

就像其他人所说的那样,您需要添加更多的@Value注释以告诉Lombok在生成的构造函数上编写@JsonCreator

@AllArgsConstructor(onConstructor_={@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)})

注意,如果您使用的是JDK8 +(与Javadoc中提到的一样),则语法与此处的其他答案略有不同

如果您使用的是freefair gradle插件,则还需要对其进行配置以在build.gradle中添加构造函数属性:

lombok {
    config['lombok.anyConstructor.addConstructorProperties'] = 'true'
}

答案 7 :(得分:0)

如果您在本机模式下运行并且收到错误是因为反射解决了用 @RegisterForReflection 注释您的类并重建应用程序并运行您的错误将得到解决

关于link的信息搜索