Jackson 自定义解串器无法在 Spring 引导中工作
Jackson Custom Deserializer Not Working In Spring Boot
我为我的实体创建了一个自定义反序列化器,但它一直抛出异常:
我有两个 类:AppUser 和 AppUserAvatar
AppUser.java
@Entity
@Table(name = "user")
public class AppUser implements Serializable {
@Transient
private static final long serialVersionUID = -3536455219051825651L;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Column(name = "password", nullable = false, length = 256)
private String password;
@JsonIgnore
@Column(name = "is_active", nullable = false)
private boolean active;
@JsonIgnore
@OneToMany(mappedBy = "appUser", targetEntity = AppUserAvatar.class, fetch = FetchType.LAZY)
private List<AppUserAvatar> appUserAvatars;
//// Getters and Setters and toString() ////
}
AppUserAvatar.java
@Entity
@Table(name = "user_avatar")
public class AppUserAvatar extends BaseEntityD implements Serializable {
@Transient
private static final long serialVersionUID = 8992425872747011681L;
@Column(name = "avatar", nullable = false)
@Digits(integer = 20, fraction = 0)
@NotEmpty
private Long avatar;
@JsonDeserialize(using = AppUserDeserializer.class)
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private AppUser appUser;
//// Getters and Setters and toString() ////
}
AppUserDeserializer.java
包裹 com.nk.accountservice.deserializer;
import com.edoctar.accountservice.config.exception.InputNotFoundException;
import com.edoctar.accountservice.domain.candidate.AppUser;
import com.edoctar.accountservice.service.candidate.AppUserService;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.io.Serializable;
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
private static final long serialVersionUID = -9012464195937554378L;
private AppUserService appUserService;
@Autowired
public void setAppUserService(AppUserService appUserService) {
this.appUserService = appUserService;
}
@Override
public Object deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
Long userId = node.asLong();
System.out.println(node);
System.out.println(node.asLong());
AppUser appUser = appUserService.findById(userId);
System.out.println("appuser: " + appUser);
if (appUser == null) try {
throw new InputNotFoundException("User not found!");
} catch (InputNotFoundException e) {
e.printStackTrace();
return null;
}
return appUser;
}
}
样本 xhr 男孩是:
{
"appUser": 1,
"avatar": 1
}
每次提交请求都抛出异常
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.edoctar.accountservice.domain.candidate.AppUserAvatar["appUser"])]
我发现 appUserService.findById() 方法没有被调用。我真的很困惑。我不知道我哪里出错了。对于任何解决方案都将非常有用。谢谢。
更新的答案:
您不能使用自动装配的属性,因为您不在 Spring 上下文中。您正在将 class AppUserDeserializer
作为注释
中的参考传递
@JsonDeserialize(using = AppUserDeserializer.class)
这种情况是FasterJackson库创建了AppUserDeserializer
的实例,所以不考虑Autowired
注解。
你可以用一个小技巧来解决你的问题。在 AppUserService
:
中添加对 spring 创建的实例的静态引用
@Service
public AppUserService {
public static AppUserService instance;
public AppUserService() {
// Modify the constructor setting a static variable holding a
// reference to the instance created by spring
AppUserService.instance = this;
}
...
}
在 AppUserDeserializer
中使用该引用:
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
private AppUserService appUserService;
public AppUserDeserializer() {
// Set appUserService to the instance created by spring
this.appUserService = AppUserService.instance;
}
...
}
原始答案:要正确初始化 Autowired
属性,您必须注释 class AppUserDeserializer
,否则 appUserService
为空,如果您没有使用 set 方法显式初始化它。
尝试用 @Component
注释 AppUserDeserializer
:
@Component // Add this annotation
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
...
}
尝试更改这行代码:
private boolean active;
到
private Boolean active;
布尔基元无法处理空值,可能会导致 NPE。
您可以继续尝试正确注入 AppUserService
,但根据我的说法,这不是最干净的解决方案。一般来说,我不喜欢使用 @Entity
作为通信模型或视图模型的想法。通过这种方式,您将实体耦合到视图模型的 producer/consumer 。您基本上是在将模型部分短路。
我会做的是在反序列化阶段将json的内容映射到不同的class,稍后使用这个来构造相应的实体。
我为我的实体创建了一个自定义反序列化器,但它一直抛出异常:
我有两个 类:AppUser 和 AppUserAvatar
AppUser.java
@Entity
@Table(name = "user")
public class AppUser implements Serializable {
@Transient
private static final long serialVersionUID = -3536455219051825651L;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Column(name = "password", nullable = false, length = 256)
private String password;
@JsonIgnore
@Column(name = "is_active", nullable = false)
private boolean active;
@JsonIgnore
@OneToMany(mappedBy = "appUser", targetEntity = AppUserAvatar.class, fetch = FetchType.LAZY)
private List<AppUserAvatar> appUserAvatars;
//// Getters and Setters and toString() ////
}
AppUserAvatar.java
@Entity
@Table(name = "user_avatar")
public class AppUserAvatar extends BaseEntityD implements Serializable {
@Transient
private static final long serialVersionUID = 8992425872747011681L;
@Column(name = "avatar", nullable = false)
@Digits(integer = 20, fraction = 0)
@NotEmpty
private Long avatar;
@JsonDeserialize(using = AppUserDeserializer.class)
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private AppUser appUser;
//// Getters and Setters and toString() ////
}
AppUserDeserializer.java 包裹 com.nk.accountservice.deserializer;
import com.edoctar.accountservice.config.exception.InputNotFoundException;
import com.edoctar.accountservice.domain.candidate.AppUser;
import com.edoctar.accountservice.service.candidate.AppUserService;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.io.Serializable;
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
private static final long serialVersionUID = -9012464195937554378L;
private AppUserService appUserService;
@Autowired
public void setAppUserService(AppUserService appUserService) {
this.appUserService = appUserService;
}
@Override
public Object deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
Long userId = node.asLong();
System.out.println(node);
System.out.println(node.asLong());
AppUser appUser = appUserService.findById(userId);
System.out.println("appuser: " + appUser);
if (appUser == null) try {
throw new InputNotFoundException("User not found!");
} catch (InputNotFoundException e) {
e.printStackTrace();
return null;
}
return appUser;
}
}
样本 xhr 男孩是:
{
"appUser": 1,
"avatar": 1
}
每次提交请求都抛出异常
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.edoctar.accountservice.domain.candidate.AppUserAvatar["appUser"])]
我发现 appUserService.findById() 方法没有被调用。我真的很困惑。我不知道我哪里出错了。对于任何解决方案都将非常有用。谢谢。
更新的答案:
您不能使用自动装配的属性,因为您不在 Spring 上下文中。您正在将 class AppUserDeserializer
作为注释
@JsonDeserialize(using = AppUserDeserializer.class)
这种情况是FasterJackson库创建了AppUserDeserializer
的实例,所以不考虑Autowired
注解。
你可以用一个小技巧来解决你的问题。在 AppUserService
:
@Service
public AppUserService {
public static AppUserService instance;
public AppUserService() {
// Modify the constructor setting a static variable holding a
// reference to the instance created by spring
AppUserService.instance = this;
}
...
}
在 AppUserDeserializer
中使用该引用:
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
private AppUserService appUserService;
public AppUserDeserializer() {
// Set appUserService to the instance created by spring
this.appUserService = AppUserService.instance;
}
...
}
原始答案:要正确初始化 Autowired
属性,您必须注释 class AppUserDeserializer
,否则 appUserService
为空,如果您没有使用 set 方法显式初始化它。
尝试用 @Component
注释 AppUserDeserializer
:
@Component // Add this annotation
public class AppUserDeserializer extends JsonDeserializer implements Serializable {
...
}
尝试更改这行代码:
private boolean active;
到
private Boolean active;
布尔基元无法处理空值,可能会导致 NPE。
您可以继续尝试正确注入 AppUserService
,但根据我的说法,这不是最干净的解决方案。一般来说,我不喜欢使用 @Entity
作为通信模型或视图模型的想法。通过这种方式,您将实体耦合到视图模型的 producer/consumer 。您基本上是在将模型部分短路。
我会做的是在反序列化阶段将json的内容映射到不同的class,稍后使用这个来构造相应的实体。