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

oalqel3c  于 2023-06-04  发布在  Java
关注(0)|答案(9)|浏览(585)

我有以下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注解,它生成一个构造函数。这意味着这个类没有无参数构造函数。
我使用Sping Boot 1.4.3.RELEASE和ObjectMapper bean能够从JSON反序列化这样的对象。
升级到Sping 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)
Sping Boot 1.4.3中使用的Jackson版本是2.8.10,Spring Boot 2.0.0.M7是2.9.2
我试着在Google上搜索这个问题,但只找到了@JsonCreator@JsonProperty的解决方案。
那么,为什么它可以在Sping Boot 1.4.3中工作,而在Spring Boot 2中失败呢?是否可以配置bean以与旧版本相同的方式运行?

vltsax25

vltsax251#

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

lombok.anyConstructor.addConstructorProperties=true

这在Lombok更新日志中描述:https://projectlombok.org/changelog
之后,@Value应该再次被Jackson接受。
您可能有兴趣在这里关注相关的GitHub问题,尽管它是关于@Data的:https://github.com/rzwitserloot/lombok/issues/1563

wmomyfyw

wmomyfyw2#

解决这个问题的另一个方法。使用Jacksonparameter names module,默认情况下包含在Sping 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 {...}
jxct1oxe

jxct1oxe3#

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

crcmnpdw

crcmnpdw4#

如果你在你的类中有一些final字段,并且你宁愿让Object creator使用特定的构造函数来反序列化你的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
}
mftmpeh8

mftmpeh85#

我遇到了这个问题,但我的解决方案并不包括添加以下内容:(mode = JsonCreator.Mode.PROPERTIES)
真正解决我的问题的是构造函数中的@JsonProperty
我的最终结果是这样的:

@Value
public class ServerTimeResponse {

    long serverTime;

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

}
iibxawm4

iibxawm46#

如果您在本机模式下运行,并且由于反射而获得错误,请使用@RegisterForReflection注解您的类并重新构建应用程序并运行,错误将得到解决。
用于链接上的信息搜索

ycl3bljg

ycl3bljg7#

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

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

注意如果您使用的是JDK8+,这里的语法与其他答案略有不同(这在javadoc中提到)

如果你使用的是freefair gradle插件,你还需要配置它在build.gradle中添加构造函数属性:

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

2wnc66cl8#

我已经升级lombok版本到:'org.projectlombok:lombok:1.18.0',它对我很有效。

相关问题