no String-argument constructor/factory method to deserialize from String value ('2018-12-14')

12,698

Solution 1

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

Solution 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

Share:
12,698

Related videos on Youtube

Nenad Bulatović
Author by

Nenad Bulatović

Software Developer and Business Analyst/Consultant Java, C/C++, C#, Linux enthusiast Blog: http://nenadbulatovic.blogspot.com/

Updated on June 04, 2022

Comments

  • Nenad Bulatović
    Nenad Bulatović almost 2 years

    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?