FasterXML / jackson-modules-java8

Set of support modules for Java 8 datatypes (Optionals, date/time) and features (parameter names)
Apache License 2.0
399 stars 116 forks source link

Cannot deserialize value of type `java.time.LocalDate` from String "07-OCT-21": Failed to deserialize java.time.LocalDate: #256

Closed Prasaddiwalkar closed 1 year ago

Prasaddiwalkar commented 1 year ago

I am getting following exception.

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDate` from String "07-OCT-21": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '07-OCT-21' could not be parsed at index 0
 at [Source: (String)"{"name":"party","eventDate":"07-OCT-21"}"; line: 1, column: 29] (through reference chain: jolt.custom.date.transformer.model.Event["eventDate"])
    at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
    at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1991)
    at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:1219)
    at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._handleDateTimeException(JSR310DeserializerBase.java:176)
    at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer._fromString(LocalDateDeserializer.java:179)
    at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:92)
    at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:38)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
    at jolt.custom.date.transformer.JacksonDateFormter.main(JacksonDateFormter.java:46)
Caused by: java.time.format.DateTimeParseException: Text '07-OCT-21' could not be parsed at index 0
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2052)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954)
    at java.base/java.time.LocalDate.parse(LocalDate.java:430)
    at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer._fromString(LocalDateDeserializer.java:177)
    ... 10 more

Scenario:- I am getting a date from downstream API in dd-MMM-yy format e.g 07-OCT-21 my requirement is my API should return date in ISO_DATE format i.e. 'yyyy-MM-dd'. We have swagger specification files using which we are generating a boilerplate code using swaggger-codegen maven plugin target. So it adds @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) annotation for date.

From mapper my expectation is while mapping json to java class it should transform date from dd-MMM-yy to yyyy-MM-dd format as per ISO_DATE format.

code to reproduce

Sample response coming from downstream application with date dd-MMM-yy format src/main/resources/event1.json

{
   "name":"party",
   "eventDate":"07-OCT-21"
}

Event.java

import org.springframework.format.annotation.DateTimeFormat;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
@EqualsAndHashCode
public class Event {
    public String name;

    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    public LocalDate eventDate;

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return getName() + " : " + getEventDate().toString();
    }
}

JacksonDateFormter.java

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import com.bazaarvoice.jolt.JsonUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;

import jolt.custom.date.transformer.model.Event;

public class JacksonDateFormter {

    public static void main(String[] args) throws ParseException, JsonMappingException, JsonProcessingException {

        Object object = JsonUtils.classpathToObject("/event1.json");

        String eventString = JsonUtils.toJsonString(object);

        ObjectMapper mapper = new ObjectMapper();

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE));

        mapper.registerModule(javaTimeModule);

        mapper.readValue(eventString, Event.class);

    }

}

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>jolt-json-transformer</groupId>
  <artifactId>jolt-json-transformer</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>jolt-json-transformer</name>
  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>com.bazaarvoice.jolt</groupId>
      <artifactId>json-utils</artifactId>
      <version>0.1.7</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.bazaarvoice.jolt/jolt-core -->
    <dependency>
      <groupId>com.bazaarvoice.jolt</groupId>
      <artifactId>jolt-core</artifactId>
      <version>0.1.7</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.0.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.24</version>
      <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
      <version>2.14.1</version>
    </dependency>

  </dependencies>
</project>
Prasaddiwalkar commented 1 year ago

any updates?

cowtowncoder commented 1 year ago

@Prasaddiwalkar seriously?

Will transfer as this belongs under Java 8 modules (which provides support for Java 8 date/time)

Prasaddiwalkar commented 1 year ago

@Prasaddiwalkar seriously?

Will transfer as this belongs under Java 8 modules (which provides support for Java 8 date/time)

Yes, I have added code snippet please try to reproduce. Let me know if you face any issue in reproducing it.

cowtowncoder commented 1 year ago

One quick note: setDateFormat() only works for java.util.Date and java.util.Calendar, not Java 8 date/time types (or Joda). Latter use @JsonFormat annotation on property, or via "Config Override" system (shown in unit tests).

Also: @DateTimeFormat is not an annotation Jackson directly knows about, being Spring annotation. I guess something in Spring might configure ObjectMapper but Jackson itself is not aware of that annotation.

cowtowncoder commented 1 year ago

Examples can be found from:

src/test/java/com/fasterxml/jackson/datatype/jsr310/deser/LocalDateDeserTest.java

so for example:

        Value format = JsonFormat.Value
                        .forPattern("dd-MMM-yy");
        mapper.configOverride(LocalDate.class).setFormat(format);

to configure pattern that might work.

The default format accepted seems to be:

DateTimeFormatter.ISO_LOCAL_DATE
cowtowncoder commented 1 year ago

I think above configuration should allow handling: will close this issue in a couple of days (unless there's an update indicating further problems).

Prasaddiwalkar commented 1 year ago

still same error

cowtowncoder commented 1 year ago

@Prasaddiwalkar same error if using configuration override I showed? That should change handling to accept values as represented, unlike original configuration.

cowtowncoder commented 1 year ago

I would need a stand-alone reproduction here -- isolated unit test. As things are, I cannot see what exactly is supposed to fail and cannot really do anything about possible problem.

May be re-opened with a reproduction with assertions (like junit test), not just description (or code that does not specify what is expected behavior, with what input etc).