JSON 解析错误:在处理一对一关系时无法构造'Custom object'实例



我正在使用java和spring boot开发一个REST API。

存在两个具有一对一关系的实体:

用户:

@Entity
@Table(name = "users")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name", nullable = false)
private String lastName;
@Column(name = "email", nullable = false, unique = true)
private String email;
@OneToOne(fetch = FetchType.LAZY,
cascade =  CascadeType.ALL,
mappedBy = "employee")
@JsonBackReference
private Company company;
// constructors, getters & setters...

公司:

@Entity
@Table(name = "companies")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Company {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Column(name = "company_name", nullable = false)
private String companyName;
@Column(name = "company_info", nullable = false)
private String companyInfo;
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_at", nullable = false)
private Date createdAt;
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "employee_id", nullable = false)
@JsonManagedReference
private User employee;
// constructors, getters & setters...

我希望能够通过POST方法创建公司,所以我在CompanyController中创建了一个(我省略了控制器中的GET方法,因为它们按预期工作):

@RestController
@RequestMapping(value = "/api/v1")
public class CompanyController {
private static final Logger logger = LoggerFactory.getLogger(CompanyController.class);
@Autowired
private CompanyRepository companyRepository;
@PostMapping(path = "company", produces = "application/JSON")
public ResponseEntity<?> createCompany(@RequestBody Company company){
logger.info("Request to save new Company: {}", company);
Company result = companyRepository.save(company);
return ResponseEntity.ok().body(result);
}

当我用JSON发送请求时,createCompany方法工作良好,如下所示:

{
"companyName" : "Big Client Company 2",
"companyInfo" : "unspecified",
"employee" : {
"id": 17,
"firstName": "someName",
"lastName": "someLastName",
"email": "someEmail@a.ru"
}
}

然而,我希望能够发送JSON,而不需要整个员工字段,只需要一个id:

{
"companyName" : "Big Client Company 2",
"companyInfo" : "unspecified",
"employee" : 17
}

当我这样做的时候,我会得到一个错误:

JSON解析错误:无法构造model.User的实例(尽管至少存在一个Creator):没有从Number value(17)反序列化的int/int参数构造函数/工厂方法

那么问题有没有办法在不将公司类"employee"更改为String(并消除一对一关系)的情况下做到这一点?

我试着找到编写自定义JSON反序列化程序的例子,但没有找到任何适合我的例子

您可以使用自定义反序列化:

public class CompanyDeserializer extends StdDeserializer<Company> { 
@Autowired
private UserRepository userRepository;
public CompanyDeserializer() { 
this(null); 
} 
public CompanyDeserializer(Class<?> vc) { 
super(vc); 
}
@Override
public Company deserialize(JsonParser jp, DeserializationContext ctxt) 
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
Company c = new Company();
//Set the rest of the attributes.
Long empId = (Long) ((IntNode) node.get("employee")).numberValue();
c.setEmployee(userRepository.findById(empId).orElse(null)));
return c;
}

}

问题是您的用户在创建它们时需要提供强制性字段(firstNamelastNameemail)。

如果没有用户,您就无法使用现在的定义创建用户。

您可以在这些字段上使用nullable = true,但最终可能会导致要保存在数据库中的数据不一致。如果让这些字段为空,那么我建议采用这种方法。

另外,请注意,混合REST控制器层和存储库层通常是一种糟糕的做法。您应该用一个服务层来分隔这些层。

最新更新