3

I am using Angular date picker to send my MVC controller a date, using a Javascript Date object which is an ISO date/time.

On deserializing java.util.Date it works like a charm and Hibernate will care about cutting that Datetime to a plain Date when inserting records.

But now that I am transitioning from java.util.Date to org.joda.time.[APPROPRIATE_CLASS_HERE] I am facing this deserialization issue.

It is my understanding that if I force DateTime in my DTOs Jackson will deserialize them correctly, while instead I prefer to drop time information when the target type is a Date.

E.g.

public class UserDto {

    private LocaLDate passwordExpirationDate;

}


{
   "username":"9493",
   "completeName":"ljdjf",
   "email":"wesf@dsgfds",
   "cultureId":"IT",
   "enabled":false,
   "passwordExpirationDate":"2017-07-13T10:00:00.000Z",
   "accountExpirationDate":"2017-07-20T10:00:00.000Z"
}

Instead I get this:

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Invalid format: "2017-07-13T10:00:00.000Z" is malformed at "T10:00:00.000Z"; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Invalid format: "2017-07-13T10:00:00.000Z" is malformed at "T10:00:00.000Z" (through reference chain: it.phoenix.web.data.dtos.admin.profile.UserDTO["passwordExpirationDate"])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:244) ~[spring-web-4.3.9.RELEASE.jar:4.3.9.RELEASE]
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Invalid format: "2017-07-13T10:00:00.000Z" is malformed at "T10:00:00.000Z" (through reference chain: it.phoenix.web.data.dtos.admin.profile.UserDTO["passwordExpirationDate"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:388) ~[jackson-databind-2.8.9.jar:2.8.9]
Caused by: java.lang.IllegalArgumentException: Invalid format: "2017-07-13T10:00:00.000Z" is malformed at "T10:00:00.000Z"
    at org.joda.time.format.DateTimeFormatter.parseLocalDateTime(DateTimeFormatter.java:900) ~[joda-time-2.9.9.jar:2.9.9]
    at org.joda.time.format.DateTimeFormatter.parseLocalDate(DateTimeFormatter.java:844) ~[joda-time-2.9.9.jar:2.9.9]
    at com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:39) ~[jackson-datatype-joda-2.8.9.jar:2.8.9]
    at com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:15) ~[jackson-datatype-joda-2.8.9.jar:2.8.9]
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504) ~[jackson-databind-2.8.9.jar:2.8.9]
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104) ~[jackson-databind-2.8.9.jar:2.8.9]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:357) ~[jackson-databind-2.8.9.jar:2.8.9]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.9.jar:2.8.9]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814) ~[jackson-databind-2.8.9.jar:2.8.9]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2938) ~[jackson-databind-2.8.9.jar:2.8.9]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:241) ~[spring-web-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    ... 92 more

Question is: is there a smart way so that Jackson can decode DateTime object into a Joda LocalDate by simply stripping the time part at default/current time zone?

Notes: - I already have Jackson Joda module dependency - Jackson is 2.8.9 - I am forced to use Java 7. In a related Java 8 project I have no such problem with java.time stuff (and Jackson JSR310 module)

usr-local-ΕΨΗΕΛΩΝ
  • 26,101
  • 30
  • 154
  • 305

3 Answers3

2

According to the error message, the date/time input is coming as 2017-07-13T10:00:00.000Z and LocalDate by default can't handle it.

You can config this format by using the com.fasterxml.jackson.annotation.JsonFormat annotation in the LocalDate field:

@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private LocalDate passwordExpirationDate;

This will make Jackson parse the date correctly.

  • Any chance to apply this worldwide... ehm... classpath-wide? – usr-local-ΕΨΗΕΛΩΝ Jul 19 '17 at 17:18
  • Also... I just finished testing a proposed solution. I will post it as answer. If you can help me apply the above to every property that is of type LocalDate I will award you the accepted answer, because I don't like my solution – usr-local-ΕΨΗΕΛΩΝ Jul 19 '17 at 17:20
  • How do you configure jackson? Do you create `ObjectMapper` manually or configure it using Angular specific config files? –  Jul 19 '17 at 17:21
  • ObjectMapper is configured via Spring as a bean (see answer). I was seeking whether there is any package-info wide annotation like Hibernate's @TypeDefs, which comes very handy – usr-local-ΕΨΗΕΛΩΝ Jul 19 '17 at 17:22
  • 1
    @usr-local-ΕΨΗΕΛΩΝ I was going to suggest exactly what you're doing. I don't use Jackson very often, so that's the only alternatives I know: annotate all fields with the desired format, or change the deserializer for all fields of that type. –  Jul 19 '17 at 17:27
  • 2
    I get this error `JSON parse error: Cannot deserialize value of type 'java.time.LocalDate' from String "2017-12-31T23:00:00.000Z"` – clemtoy Oct 19 '18 at 13:59
1

The following worked to me in my Spring context configuration

<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" id="pnxObjectMapper">
    <property name="deserializersByType">
        <map key-type="java.lang.Class">
            <entry>
                <key>
                    <value>org.joda.time.LocalDate</value>
                </key>
                <bean class="com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer">
                    <constructor-arg>
                        <util:constant static-field="com.fasterxml.jackson.datatype.joda.cfg.FormatConfig.DEFAULT_DATETIME_PARSER" />
                    </constructor-arg>
                </bean>
            </entry>
        </map>
    </property>
</bean>

Explanation: before loading the JodaModule, which defines a deserializer for LocalDates rightfully adopting an ISO date-only format, I force Spring to change the formatter when constructing the serializer

usr-local-ΕΨΗΕΛΩΝ
  • 26,101
  • 30
  • 154
  • 305
0

I think you when you create ObjectMapper you can register some modules.

public ObjectMapper objectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    objectMapper.registerModule(new JavaTimeModule());
    objectMapper.registerModule(new JodaModule());
    return objectMapper;
}

And put that in message convertor in Spring Config

MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper());

// Configuration class
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(mappingJackson2HttpMessageConverter());
}

After it should work

sendon1982
  • 9,982
  • 61
  • 44