2

This question is almost same as this one, but it differs in that I am trying to get String to LocalDate. Here is error from 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"])]

and here is from 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" }

And POST request was:

{
    "id": 12345,
    "checkin": "2018-12-14",
    "checkout": "2018-12-17"
}

Where relevant classes are:

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();
    }
}

and

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;
    }
}

and

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);
    }
}

I've included imports just in case.

Anyway, if I change ReservationRequest to have fields with Strings instead of LocalDate like this then it doesn't produce error

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'; name: 'jackson-datatype-jsr310', version: '2.9.7'

Question is why it doesn't work as intended with LocalDate?

UPDATE: tried these solutions, and added @JsonSerialize and @JsonDeserialize, as neither objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); or objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

worked so now it looks like:

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();
    }

So, now it looks like it works but I don't know if it is good solution?

2

3 Answers 3

4

I think the problem is here

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new JavaTimeModule());
    return new ObjectMapper();
}

You must return configured objectMapper instead of new instance:

@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;
}
Sign up to request clarification or add additional context in comments.

Comments

2

I faced the same issue recently and reason for the error was there are double quotes around my json string which when I removed it worked perfectly fine

Comments

-2

I think its too late for me to reply on the post I have faced the same issue the below annotation was helpful.

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dateOfBirth;

:)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.