Java Jackson何时不需要参数构造函数进行反序列化?
在我的春季长靴项目中,我注意到杰克逊有一种奇怪的行为。我在互联网上搜索,找到了该做什么,但还没有找到原因 用户地址:Java Jackson何时不需要参数构造函数进行反序列化?,java,json,spring-boot,jackson,lombok,Java,Json,Spring Boot,Jackson,Lombok,在我的春季长靴项目中,我注意到杰克逊有一种奇怪的行为。我在互联网上搜索,找到了该做什么,但还没有找到原因 用户地址: @Setter @Getter @AllArgsConstructor public class UserDto { private String username; private String email; private String password; private String name; private String
@Setter
@Getter
@AllArgsConstructor
public class UserDto {
private String username;
private String email;
private String password;
private String name;
private String surname;
private UserStatus status;
private byte[] avatar;
private ZonedDateTime created_at;
}
添加一个新用户就可以了
标记:
@Setter
@Getter
@AllArgsConstructor
public class TagDto {
private String tag;
}
尝试添加新标记时出错:
com.fasterxml.jackson.databind.exc.MismatchedInputException:无法构造TagDto的实例(尽管至少存在一个创建者):无法从对象值反序列化(无委托或基于属性的创建者)
该问题的解决方案是向TagDto类添加零参数构造函数
为什么Jackson在TagDto中进行反序列化时不需要arg构造函数,而在UserDto中却可以正常工作
使用相同的方法将两者相加。
我的标记和用户实体都带有注释
@Entity
@Setter
@Getter
@NoArgsConstructor
并具有所有args构造函数:
@Entity
@Setter
@Getter
@NoArgsConstructor
public class User extends AbstractModel {
private String username;
private String password;
private String email;
private String name;
private String surname;
private UserStatus status;
@Lob
private byte[] avatar;
@Setter(AccessLevel.NONE)
private ZonedDateTime created_at;
public User(final String username, final String password, final String email, final String name, final String surname) {
this.username = username;
this.password = password;
this.email = email;
this.name = name;
this.surname = surname;
this.created_at = ZonedDateTime.now();
}
}
@Entity
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Tag extends AbstractModel {
private String tag;
}
@MappedSuperclass
@Getter
public abstract class AbstractModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
}
实体生成:
@PostMapping(path = "/add")
public ResponseEntity<String> add(@Valid @RequestBody final D dto) {
this.abstractModelService.add(dto);
return new ResponseEntity<>("Success", HttpStatus.CREATED);
}
public void add(final D dto) {
//CRUD repository save method
this.modelRepositoryInterface.save(this.getModelFromDto(dto));
}
@Override
protected Tag getModelFromDto(final TagDto tagDto) {
return new Tag(tagDto.getTag());
}
@Override
protected User getModelFromDto(final UserDto userDto) {
return new User(userDto.getUsername(), userDto.getPassword(), userDto.getEmail(), userDto.getName(), userDto.getSurname());
}
通过邮递员本地主机发送:8081/tag/add,返回
{
"timestamp": "2020-09-26T18:50:39.974+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/tag/add"
}
我在Jackson v2.11.2中使用Lombok v1.18.12和Spring boot 2.3.3.RELEASE;医生:解决方案就在最后
Jackson支持多种创建POJO的方法。以下列出了最常见的方法,但可能不是一个完整的列表:
公共类Foo{
私有int-id;
public int getId(){返回this.id;}
@JsonProperty
public void setId(int id){this.id=id;}
}
指定是可选的,但可用于微调映射,以及诸如
公共类Foo{
私有int-id;
@JsonCreator
public Foo(@JsonProperty(“id”)int-id){
this.id=id;
}
公共int getId(){
返回此.id;
}
}
为构造函数指定是可选的,但我认为如果有多个构造函数,则必须指定。为参数指定@JsonProperty
是可选的,但如果类文件中未包含参数名,则命名属性时需要指定该参数(-parameters
编译器选项)
这些参数意味着这些属性是必需的。可以使用setter方法设置可选属性
公共类Foo{
私有int-id;
@JsonCreator
公共静态Foo-create(@JsonProperty(“id”)int-id){
返回新的Foo(id);
}
私人Foo(int-id){
this.id=id;
}
公共int getId(){
返回此.id;
}
}
String
构造函数从文本值创建实例
公共类Foo{
私有int-id;
@JsonCreator
公共Foo(字符串str){
this.id=Integer.parseInt(id);
}
公共int getId(){
返回此.id;
}
@JsonValue
公共字符串asJsonValue(){
返回整数.toString(this.id);
}
}
当POJO具有简单的文本表示时,这非常有用,例如,LocalDate
是具有3个属性(year
,month
,dayOfMonth
)的POJO,但通常最好将其序列化为单个字符串(yyyy-MM-dd
格式)。标识序列化期间要使用的方法,@JsonCreator
标识反序列化期间要使用的构造函数/工厂方法
注意:这也可以用于使用JSON值而不是字符串
的单值构造,但这种情况非常罕见
UserDto
之所以有效,是因为只有一个构造函数(因此不需要@JsonCreator
)和许多参数(因此不需要@JsonProperty
)
但是,对于TagDto
只有一个没有任何注释的单参数构造函数,因此Jackson将该构造函数分类为类型4(从我上面的列表中),而不是类型2
这意味着它期望POJO是一个值类,其中封闭对象的JSON是{…,“tag”:“value”,…}
,而不是{…,“tag”:{“tag”:“example”}
要解决此问题,您需要通过在构造函数参数上指定@JsonProperty
来告诉Jackson构造函数是属性初始化构造函数(#2),而不是值类型构造函数(#4)
这意味着您不能让Lombok为您创建构造函数:
@Setter
@吸气剂
公共类TagDto{
私有字符串标签;
公共标记到(@JsonProperty(“标记”)字符串标记){
this.tag=tag;
}
}
Jackson默认情况下不会使用带参数的构造函数,您需要告诉它在注释中这样做。默认情况下,它会尝试使用类中不存在的无参数构造函数。@Thomas UserDto如何正常工作?这是个好问题。我不是Lombox专家,所以我不得不猜测,但一个原因可能是TagDto
只有一个属性可能会导致问题。有时这些库会出现或导致错误,例如,提供有关您正在使用的版本的更多信息可能会更好。您可以添加完整的代码吗?@Entity类看起来怎么样?你到底是怎么储存的?如果合适,Jackson可能会尝试使用AllArgsConstructor。可能是Jackson对一个实体使用了所有参数,而对另一个实体没有使用任何参数。这取决于如何实例化这些DTO。尝试删除Lombok注释并提供自己的构造函数。然后在这些文件中添加一些print语句,然后自己查看它(称为when)。我仍然看不到如何将实体转换为DTO,以及如何添加它们。请提供并告诉我们-您何时/何地出现错误?当您转换为DTO时,很可能是这样,对吗?还有,你的鳕鱼
{
"timestamp": "2020-09-26T18:50:39.974+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/tag/add"
}