没有创建者,比如默认构造函数,存在): 无法从对象值反序列化(没有基于委托或属性的创建者)

334

我正在尝试使用Retrofit和Jackson进行反序列化来调用API,但是我遇到了onFailure错误 No Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator


2
你似乎没有使用适用于 Kotlin 的 Jackson 模块,该模块可与没有默认构造函数的数据类一起使用,而你的数据类正是这种情况。请查看该模块并检查是否存在任何问题。 - Jayson Minard
请更改已接受的答案。 - Noumenon
这是因为Jackson(Spring WebClient使用的JSON反序列化库)尝试仅反序列化到具有非默认构造函数的类中,在这种情况下,它无法知道要用于JSON属性的参数。只有在存在调试符号和一些额外的Jackson模块的情况下才可能实现。在这种情况下,Jackson的正常方式是在类中的此非默认构造函数上使用@JsonCreator注释进行反序列化。 - Krzysztof Tomaszewski
32个回答

321

原因: 此错误发生是因为Jackson库不知道如何创建您的模型,该模型没有空构造函数,并且模型包含一个带有参数的构造函数,但未对其参数进行注释@JsonProperty("field_name")。如果您的类没有添加构造函数,默认情况下Java编译器会创建一个空构造函数。

解决方案: 向您的模型添加一个空构造函数或使用@JsonProperty("field_name")对构造函数参数进行注释。

如果您使用的是Kotlin数据类,则还可以使用@JsonProperty("field_name")进行注释,或者将jackson module kotlin注册到ObjectMapper中。

您可以使用http://www.jsonschema2pojo.org/创建您的模型。


10
这有效的原因是它们不是数据类。 - mkki
5
这是解决方案。但哪种方式更好?一般来说,创建一个空的构造函数还是添加JSON属性? - O. Schnieders
4
@Maximus,我认为如果你指的是性能更好,那么在性能方面没有区别。创建一个空构造函数比在每个参数上添加注释更容易。 - Bek
1
在 Kotlin 项目中遇到了这个问题,使用名为 'name' 的字段时也失败了,我用 @JsonProperty("name") 注释了该字段,问题得到了解决。 - Albert Scholtz
对我来说没有起作用。 - undefined
显示剩余6条评论

116

我在寻找以下错误而来到这里:

No Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator

与Retrofit无关,但是如果您正在使用Jackson,则可以通过向抛出错误的类添加默认构造函数来解决此错误。

更多信息请参见:https://www.baeldung.com/jackson-exception

上述链接中最相关的部分:

“如果Jackson无法访问构造函数,则会引发此异常。当我们尝试反序列化JSON字符串时,会抛出JsonMappingException: No Suitable Constructor Found。为了解决这个问题,我们只需添加一个默认构造函数:”

    public Foo() {
        super();
    }

现在当我们反序列化时,该过程将正常工作。


26
如果您希望对象不可变,您不应该创建一个默认构造函数。在构造函数上添加@JsonCreator@ConstructorProperties({"prop1Name", "propr2Name"})可以解决上述错误。 - Kirill
4
如果您正在使用Lombok和@Data,请确保您的字段不是final,否则将无法使用空构造函数。 这样是行不通的: @Data static class LoginResponse { private final String token,message; private final int status; } 但是这样可以: @Data static class LoginResponse { private String token, message; private int status; } - Vladtn
1
你能否从这篇文章中添加一些简要细节呢?虽然从得分来看,这显然对人们有所帮助,但它在技术上只是一个链接式答案。我不想因为这么微小的事情而看到它被删除。 - Captain Man
完成,希望有所帮助。 - Fede Mika

109

如果你在一个 POJO 模型中使用 Lombok,请确保你有以下注解:

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor

可能会有所不同,但请确保@Getter,尤其是@NoArgsConstructor


9
@NoArgsConstructor 可以解决我的问题。我的完整注释如下:@Data @AllArgsConstructor @NoArgsConstructor @Entity @JsonInclude(Include.NON_DEFAULT) - davide
7
@Data 相当于 @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode。因此可能只需要 @NoArgsConstructor - 54l3d
@54l3d 如果您在@Data注释中指定另一个构造函数注释,则不会包括@RequiredArgsConstructor - payne
6
我想指出你救了我的一天:),这段时间我一直缺少@NoArgsConstructor注释。 - Pol Ortiz
3
在我的情况下,@NoArgsConstructor@AllArgsConstructor都是必需的。谢谢! - Fatmajk
显示剩余4条评论

76

你需要使用jackson-module-kotlin来反序列化数据类。详情请参见这里

如果尝试将一些值反序列化为数据类时没有启用该模块,或者即使启用了该模块但使用的ObjectMapper没有注册KotlinModule,则上面的错误消息是Jackson提供的。例如,看下面的代码:

data class TestDataClass (val foo: String)

val jsonString = """{ "foo": "bar" }"""
val deserializedValue = ObjectMapper().readerFor(TestDataClass::class.java).readValue<TestDataClass>(jsonString)

这将会导致以下错误:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `test.SerializationTests$TestDataClass` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

如果您更改上面的代码,并将ObjectMapper替换为jacksonObjectMapper(它只是返回一个已注册KotlinModule的普通ObjectMapper),它可以工作。即:

val deserializedValue = jacksonObjectMapper().readerFor(TestDataClass::class.java).readValue<TestDataClass>(jsonString)

对于Android方面,我不确定,但看起来您需要让系统使用jacksonObjectMapper来进行反序列化。


我能把代码仓库传给你看一下吗? - José Nobre
10
问题肯定与未使用该模块和使用数据类有关。因此,您需要在系统使用的对象映射器上注册该模块。在您使用的框架中查找可以修改对象映射器配置以调用mapper.registerModule(KotlinModule())的位置。 - Jayson Minard
5
José和Jayson在写有关JSON的内容 :-D - programaths
@JanacMeena,这个答案中的解决方案特定于Kotlin,但是如果Java类以某种特定方式设置(例如仅具有私有构造函数或类似的设置),则其本身可能会在反序列化时发生错误。 我没有尝试过,所以不确定。 - Yoni Gibbs
我正在使用Quarkus,配合Kotlin,并且REST API端点的body使用了数据类对象。添加jackson-module-kotlin解决了问题。 - Yazid
显示剩余3条评论

19

我正在使用Quarkus、Jackson和Lombok。因此,我通过在模型类上添加@Jacksonized属性来解决了这个问题。 所以所有的属性都是:

@Jacksonized //missing
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ...

1
@Jacksonized总是与@Builder(Lambok)一起使用,因此需要同时添加这两个才能正常工作。在我的情况下,我的请求体只有一个名为iBan的请求实体。因此,当我尝试从请求中检索数据时,它会调用noargs构造函数...结果我无法获取有效载荷数据... - Muahmmad Tayyib

15

我遇到了类似的问题(使用Jackson、lombok和gradle),并且有一个没有无参构造函数的POJO - 解决方法是添加

lombok.anyConstructor.addConstructorProperties=true

lombok.config 文件中


1
如果您正在使用@Value,这似乎是最好的选择...没有额外的注释,只需使您的不可变类可反序列化。 - Ilan Segal

13

我知道这是一个旧的帖子,但对于使用 Retrofit 的任何人来说,这可能很有用。

如果您正在使用 Retrofit + Jackson + Kotlin + Data classes,则需要:

  1. implement group: 'com.fasterxml.jackson.module',name:'jackson-module-kotlin',version:'2.7.1-2' 添加到依赖项中,以便 Jackson 可以将其反序列化为数据类
  2. 在构建Retrofit时,传递 Kotlin Jackson Mapper,以便Retrofit使用正确的映射器,例如:
    val jsonMapper = com.fasterxml.jackson.module.kotlin.jacksonObjectMapper()

    val retrofit = Retrofit.Builder()
          ...
          .addConverterFactory(JacksonConverterFactory.create(jsonMapper))
          .build()

注意:如果没有使用Retrofit,@Jayson Minard提供了一种更通用的方法。


1
太好了!我认为Retrofit正在使用Jackson的其他实例。 - Andres
1
真希望我能给你一些声望点,因为你在额外时间的最后一刻救了我!!谢谢 - d4c0d312

11

我正在使用Lombok。 我之所以出现错误,是因为我没有在我的模型类中放置@NoArgsConstructor


10

我通过添加一个无参构造函数解决了这个问题。如果您使用Lombok,您只需要添加@NoArgsConstructor注释:

@AllArgsConstructor
@NoArgsConstructor
@Getter
@ToString
@EqualsAndHashCode
public class User {
    private Long userId;
    private String shortName;
}

9
如果您使用lombok,您可以使用@Jacksonized注释。 不需要setters - 此注释与@Value很好配合使用。 不需要@NoArgsConstructor - 您可以使用@Builder或仅使用@RequiredArgsConstructor。

我认为在使用@Builder时,严格最少要求的是只有@Jacksonized(没有其他的,比如@Value,是绝对必要的)。参考链接 - cellepo
@cellepo,你能否请您重新表述一下您的信息吗? - Oleg Poltoratskii
3
在使用 @Builder 时,使用 @Jacksonized 可选(可能用于其他目的),但不是 Jackson-izing 本身所必需的。 - cellepo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接