这个问题和 this 几乎一样一,但不同之处在于我试图将 String 获取到 LocalDate。这是来自 STS 的错误:
2018-12-14 00:47:04.507 WARN 6216 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('2018-12-14'); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('2018-12-14') at [Source: java.io.PushbackInputStream@73ff9989; line: 3, column: 16] (through reference chain: com.xxxxx.xxxxxx.model.request.ReservationRequest["checkin"])]
这是来自 postman 的:
{ "timestamp": 1544744824516, "status": 400, "error": "Bad Request", "exception": "org.springframework.http.converter.HttpMessageNotReadableException", "message": "JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('2018-12-14'); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('2018-12-14')\n at [Source: java.io.PushbackInputStream@73ff9989; line: 3, column: 16] (through reference chain: com.xxxxx.xxxxx.model.request.ReservationRequest[\"checkin\"])", "path": "/room/reservation/v1" }
POST 请求是:
{
"id": 12345,
"checkin": "2018-12-14",
"checkout": "2018-12-17"
}
相关类在哪里:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
@Configuration
public class ApiConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return new ObjectMapper();
}
@Bean
public ObjectWriter objectWriter(ObjectMapper objectMapper) {
return objectMapper.writerWithDefaultPrettyPrinter();
}
}
和
import java.time.LocalDate;
import org.springframework.format.annotation.DateTimeFormat;
public class ReservationRequest {
private Long id;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate checkin;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate checkout;
public ReservationRequest() {
super();
}
public ReservationRequest(Long id, LocalDate checkin, LocalDate checkout) {
super();
this.id = id;
this.checkin = checkin;
this.checkout = checkout;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public LocalDate getCheckin() {
return checkin;
}
public void setCheckin(LocalDate checkin) {
this.checkin = checkin;
}
public LocalDate getCheckout() {
return checkout;
}
public void setCheckout(LocalDate checkout) {
this.checkout = checkout;
}
}
和
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.xxxxx.xxxxxx.model.request.ReservationRequest;
import com.xxxxx.xxxxxx.model.response.ReservationResponse;
@RestController
@RequestMapping(ResourceConstants.ROOM_RESERVATION_V1)
public class ReservationResource {
@RequestMapping(path = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<ReservationResponse> getAvaiableRooms(
@RequestParam(value = "checkin") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate checkin,
@RequestParam(value = "checkout") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate checkout) {
return new ResponseEntity<>(new ReservationResponse(), HttpStatus.OK);
}
@RequestMapping(path = "", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<ReservationResponse> createReservation(@RequestBody ReservationRequest reservationRequest) {
return new ResponseEntity<>(new ReservationResponse(), HttpStatus.CREATED);
}
@RequestMapping(path = "", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<ReservationResponse> updateReservation(@RequestBody ReservationRequest reservationRequest) {
return new ResponseEntity<>(new ReservationResponse(), HttpStatus.OK);
}
@RequestMapping(path = "/{reservationId}", method = RequestMethod.DELETE)
public ResponseEntity<Void> deleteReservation(@PathVariable long reservationId) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
为了以防万一,我已经包含了导入内容。
无论如何,如果我将 ReservationRequest 更改为具有字符串字段而不是像这样的 LocalDate 字段,那么它不会产生错误
public class ReservationRequest {
private Long id;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private String checkin;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private String checkout;
public ReservationRequest() {
super();
}
public ReservationRequest(Long id, String checkin, String checkout) {
super();
this.id = id;
this.checkin = checkin;
this.checkout = checkout;
}
(getters and setters updated as well)
JDK 1.8; springBootVersion = '1.5.17.RELEASE';名称:'jackson-datatype-jsr310',版本:'2.9.7'
问题是为什么它不能按照 LocalDate 的预期工作?
更新:尝试过these解决方案,并添加了 @JsonSerialize 和 @JsonDeserialize,因为两者都不是 objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
或 objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
工作了,现在看起来像:
public class ReservationRequest {
private Long id;
@JsonSerialize(using = ToStringSerializer.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
//@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate checkin;
@JsonSerialize(using = ToStringSerializer.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
//@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate checkout;
public ReservationRequest() {
super();
}
所以,现在看起来可行,但我不知道这是否是一个好的解决方案?
最佳答案
我认为问题出在这里
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return new ObjectMapper();
}
您必须返回配置的objectMapper
而不是新实例:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
objectMapper.registerModule(new JavaTimeModule()
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("M/d/yyyy")))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("M/d/yyyy"))))
return objectMapper;
}
关于spring-boot - 没有字符串参数构造函数/工厂方法来从字符串值 ('2018-12-14' 反序列化),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53771807/