Jackson反序列化报错:构造函数参数无属性名问题排查与解决
Jackson反序列化报错:InvalidDefinitionException 问题排查与解决
问题场景
使用Jackson反序列化JSON时抛出如下错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type `stack.overflow.model.dto.response.QuestionCommentResponseDto`: Argument #0 of constructor [constructor for `stack.overflow.model.dto.response.QuestionCommentResponseDto` (6 args), annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)} has no property name (and is not Injectable): can not use as property-based Creator at [Source: (String)"{"data":{"id":1,"questionId":1,"createdDate":"2023-06-03T20:16:57.238883","modifiedDate":"2023-06-03T20:16:57.238883","text":"text","owner":{"id":1,"username":"mickey_m"}}}";; line: 1, column: 1]
涉及的DTO代码如下:
QuestionCommentResponseDto
@Getter public class QuestionCommentResponseDto { private final Long id; private final Long questionId; private final LocalDateTime createdDate; private final LocalDateTime modifiedDate; private final String text; @Setter private AccountResponseDto owner; public QuestionCommentResponseDto(Long id, Long questionId, LocalDateTime createdDate, LocalDateTime modifiedDate, String text) { this.id = id; this.questionId = questionId; this.createdDate = createdDate; this.modifiedDate = modifiedDate; this.text = text; } @JsonCreator private QuestionCommentResponseDto(Long id, Long questionId, LocalDateTime createdDate, LocalDateTime modifiedDate, String text, AccountResponseDto owner) { this(id, questionId, createdDate, modifiedDate, text); this.owner = owner; } // equals() and hashcode() }
AccountResponseDto
// 关联DTO @AllArgsConstructor @NoArgsConstructor @Getter @Setter public class AccountResponseDto { private Long id; private String username; // equals() and hashcode() }
错误含义与疑问解答
- 错误含义:Jackson无法使用你标注了
@JsonCreator的6参数私有构造函数完成反序列化,原因是该构造函数的参数未绑定对应JSON属性名,Jackson无法将JSON中的键值对映射到构造函数参数上。 - Argument #0是否指id:是的,参数索引从0开始计数,Argument #0就是构造函数的第一个参数
id。
问题原因
Jackson使用@JsonCreator注解的构造函数时,默认需要明确每个参数对应的JSON属性名。你仅给构造函数添加了@JsonCreator,但未给每个参数标注@JsonProperty指定对应JSON键名,导致Jackson无法自动匹配参数与JSON字段。
解决方案
方式一:给构造函数参数添加@JsonProperty注解
给@JsonCreator标注的构造函数的每个参数加上@JsonProperty,明确对应JSON属性名:
@JsonCreator private QuestionCommentResponseDto( @JsonProperty("id") Long id, @JsonProperty("questionId") Long questionId, @JsonProperty("createdDate") LocalDateTime createdDate, @JsonProperty("modifiedDate") LocalDateTime modifiedDate, @JsonProperty("text") String text, @JsonProperty("owner") AccountResponseDto owner ) { this(id, questionId, createdDate, modifiedDate, text); this.owner = owner; }
方式二:使用Jackson参数名称模块(推荐)
若项目基于Java 8+,可引入Jackson参数名称模块,让Jackson通过反射直接获取参数名,无需手动添加@JsonProperty:
- 引入Maven依赖:
<dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-parameter-names</artifactId> <version>你的Jackson版本</version> </dependency>
- 注册模块到ObjectMapper(Spring项目可通过配置类自动注册):
@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { return new ObjectMapper() .registerModule(new ParameterNamesModule()); } }
额外优化方案
由于owner字段已有@Setter,可直接移除6参数私有构造函数,仅给5参数构造函数添加@JsonCreator(配合@JsonProperty或参数名称模块),Jackson会在实例化后自动通过setter注入owner字段:
@Getter public class QuestionCommentResponseDto { private final Long id; private final Long questionId; private final LocalDateTime createdDate; private final LocalDateTime modifiedDate; private final String text; @Setter private AccountResponseDto owner; @JsonCreator public QuestionCommentResponseDto( @JsonProperty("id") Long id, @JsonProperty("questionId") Long questionId, @JsonProperty("createdDate") LocalDateTime createdDate, @JsonProperty("modifiedDate") LocalDateTime modifiedDate, @JsonProperty("text") String text ) { this.id = id; this.questionId = questionId; this.createdDate = createdDate; this.modifiedDate = modifiedDate; this.text = text; } // equals() and hashcode() }
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

