0

I am trying to use postman and put these values into the database, but I keep getting an exception.

what im trying to deserialize from postman:

{
    "end_date": "2443-11-34 12:43:23",
    "start_date":  "2443-11-34 12:43:23"
}

The exception that I get:

2020-05-20 10:55:04.572  WARN 4452 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver :
 Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot 
deserialize value of type `java.time.Instant` from String "2443-11-34 12:43:23": Failed to deserialize 
java.time.Instant: (java.time.format.DateTimeParseException) Text '2443-11-34 12:43:23' could not be 
parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot 
deserialize value of type `java.time.Instant` from String "2443-11-34 12:43:23": Failed to deserialize 
java.time.Instant: (java.time.format.DateTimeParseException) Text '2443-11-34 12:43:23' could not be parsed at index 10
 at [Source: (PushbackInputStream); line: 3, column: 17] (through reference chain: 
com.project.rushhour.model.post.AppointmentPostDTO["end_date"])]

appointment entity:

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Appointment extends BaseEntity {

   @NotNull
   @DateTimeFormat(style = "yyyy-MM-dd HH:mm:ss")
   @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
   @JsonDeserialize(using = JacksonInstantDeserializer.class)
   private Instant startDate;

   @NotNull
   @JsonDeserialize(using = JacksonInstantDeserializer.class)
   @DateTimeFormat(style = "yyyy-MM-dd HH:mm:ss")
   @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
   private Instant endDate;

My appointmentDto class:

@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract class AppointmentDTO extends BaseDTO {

    @JsonProperty("start_date")
    private Instant startDate;

    @JsonProperty("end_date")
    private Instant endDate;

My AppointmentGetDTO class that I use

public class AppointmentGetDTO extends AppointmentDTO {
}

I also have all of the jackson dependencies

My custom deserializer that I use:

public class JacksonInstantDeserializer extends StdDeserializer<Instant> {
    public JacksonInstantDeserializer() { this(null); }
    public JacksonInstantDeserializer(Class<?> clazz) { super(clazz); }

    @Override
    public Instant deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
        return Instant.parse(parser.getText());
    }
}
cubick
  • 293
  • 3
  • 13
mkashi
  • 59
  • 1
  • 7

1 Answers1

0

You should create a CustomDeserializer for your class AppointmentGetDTO and not Instant class.

We need to register the AppointmentDTO class with the Deserializer. Below I have provided relevant code changes. User debugger and create breakpoints in the deserializer to test the conversion logic.

For further reading checkout: jackson-deserialization and this Stackoverflow answer

Read this for alternate approach: JsonComponent

AppointmentDTO:

@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract class AppointmentDTO {

    @JsonProperty("start_date")
    private Instant startDate;

    @JsonProperty("end_date")
    private Instant endDate;
}

AppointmentGetDTO:

@JsonDeserialize(using= JacksonInstantDeserializer.class)
public class AppointmentGetDTO extends AppointmentDTO {
    public AppointmentGetDTO(Instant s, Instant e) {
        super(s,e);
    }
}

Custom Deserializer for AppointmentGetDTO

public class JacksonInstantDeserializer extends StdDeserializer<AppointmentGetDTO> {
    public JacksonInstantDeserializer() { this(AppointmentDTO.class); }
    public JacksonInstantDeserializer(Class<?> clazz) { super(clazz); }

    @Override
    public AppointmentGetDTO deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
        JsonNode node = parser.getCodec().readTree(parser);
        Instant s=null;
        Instant e=null;
        if(node.get("start_date") != null) {
            s=Instant.parse(node.get("start_date").asText());
        }
        if(node.get("end_date")!=null) {
            e=Instant.parse(node.get("end_date").asText());
        }
        return new AppointmentGetDTO(s,e);
    }
}

Then you can create bean of com.fasterxml.jackson.databind.Module like below:

    @Bean
    public Module dynamoDemoEntityDeserializer() {
        SimpleModule module = new SimpleModule();
        module.addDeserializer(AppointmentGetDTO.class, new JacksonInstantDeserializer());
        return module;
    }

Controller Mapping for the request:

@PostMapping
public AppointmentDTO convert(@RequestBody  AppointmentGetDTO appointmentDTO) {
    System.out.println(appointmentDTO.getStartDate());
    System.out.println(appointmentDTO.getEndDate());
    return appointmentDTO;
}

request json:

{
    "end_date": "2443-11-12T12:43:23Z",
    "start_date":  "2443-11-12T12:43:23Z"
}
Mohd Waseem
  • 1,244
  • 2
  • 15
  • 36