Hot questions for Using Joda-Time in jackson

Question:

This is the format of the date in JSON that I want to serialize/deserialize:

"2014-06-18T06:26:56-07:00"

The Joda DateTime field is declared as follows:

  @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
    private DateTime dueTime;

The mapper:

ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT)
            .registerModule(new JodaModule())
            .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

mapper.writeValueAsString(objectWithDT)).as("application/json")

In the resulting JSON the date with timezone above changes to:

2014-06-18T13:26:56+0000

Answer:

DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE is a deserialization feature and it's not taken into consideration when performing a serialization.

One possible solution is to create an ObjectMapper instance with a TimeZone:

ObjectMapper mapper =  new ObjectMapper()
            .enable(SerializationFeature.INDENT_OUTPUT)
            .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
            .registerModule(new JodaModule())
            .setTimeZone(TimeZone.getTimeZone("GMT-7"));

For more details, check the DateTimeSerializer code.

Question:

I'm trying to serialize and deserialize quite simple object with custom date format:

public class DateTimeTest {
    private static final String DATE_PATTERN = "yyyyMMdd";

    public static DateTime date = DateTime.now();

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JodaModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        ObjectWriter writer = mapper.writer();

        String str = writer.writeValueAsString(new Domain());
        System.out.println(str);

        ObjectReader reader = mapper.reader(Domain.class);
        Domain domain = reader.readValue(str);

        System.out.println(domain.getDate());
    }

    private static class Domain {
        @JsonFormat(pattern = DATE_PATTERN)
        private DateTime date;

        public Domain() {
            this.date = DateTime.now();
        }

        public DateTime getDate() {
            return date;
        }

        public void setDate(DateTime date) {
            this.date = date;
        }
    }
}

While executing main method I'm expecting to get something similar to:

"date":"20151117" 20151117

But unfortunately get the following:

{"date":"20151117"} 20151117-01-01T00:00:00.000+03:00 (year is incorrect)

Seems that Jackson ignores @JsonFormat annotation for objects deserialization and treat the string as date in ISO-8601 notation. Does anyone know the fix?

<jackson.version>2.5.4</jackson.version>
<jodatime.version>2.8.1</version>

UPDATE: If I change date pattern to "dd/MM/yyyy", then I even start getting error "IllegalArgumentException: Invalid format". So for sure Jackson ignores date pattern for deserialization.


Answer:

According to Jackson Release Notes support for Joda @JsonFormat(pattern=...) for deserialization was added only from 2.6.0 version.

Question:

I have the following two dates:

  • 8 Oct. 2009
  • 13 May 2010

I am using Jackson to convert the date from an rest api to joda Datetime.

I thought the pattern "dd MMM. yyyy" would work but the "may" has no dot so it crashes at that point.

Is there a solution or do I have to write my own datetime parser?

The annotation in jackson is:

@JsonFormat(pattern = "dd MMM. yyyy", timezone = "UTC", locale = "US", )
@JsonProperty(value = "date")
private DateTime date;

So there is only one date pattern allowed.


Answer:

Given OP's new comment and requirements, the solution is to use a custom deserializer:

You would do something like this:

@JsonDeserialize(using = MyDateDeserializer.class)
class MyClassThatHasDateField {...}

See tutorial here: http://www.baeldung.com/jackson-deserialization

See an example here: Custom JSON Deserialization with Jackson

OLD ANSWER:

You can use Java's SimpleDateFormat and either:

  1. Use a regex to choose the proper pattern
  2. Simply try them and catch (and ignore) the exception

Example:

String[] formats = { "dd MMM. yyyy", "dd MM yyyy" };

for (String format : formats)
{
    try
    {
        return new SimpleDateFormat( format ).parse( theDateString );
    }
    catch (ParseException e) {}
}

OR

String[] formats = { "dd MMM. yyyy", "dd MM yyyy" };
String[] patterns = { "\\d+ [a-zA-Z]+\. \d{4}", "\\d+ [a-zA-Z]+ \d{4}" };

for ( int i = 0; i < patterns.length; i++ )
{
  // Create a Pattern object
  Pattern r = Pattern.compile(patterns[ i ] );

  // Now create matcher object.
  Matcher m = r.matcher( theDateString );

  if (m.find( )) {
     return new SimpleDateFormat( formats[ i ] ).parse( theDateString );
  }
}

Question:

I am trying to do a simple exercise of converting String into Joda DateTime using Faster XML (Jackson) https://github.com/FasterXML/jackson

I am able to convert it if the String format is like "1972-12-28T12:00:01.000Z". But as soon as I convert the format to simple "09/23/2016" (MM/dd/yyyy) the code fails. Following is my code:

public static void main( String[] args )
    throws JsonParseException, JsonMappingException, IOException {

    final String INPUT_JSON = "{\"start\" : \"11/23/2016\"}";
    // final String INPUT_JSON = "{\"start\" : \"1972-12-28T12:00:01.000Z\"}";
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule( new JodaModule() );
    DateFormat dateFormat = new SimpleDateFormat( "MM/dd/yyyy" );
    objectMapper.setDateFormat( dateFormat );
    Bean bean = objectMapper.readValue( INPUT_JSON, Bean.class );
    DateTime start = bean.getStart();
    System.out.println( start );
}

public class Bean {
   public DateTime start;

   public Bean() {
    // TODO Auto-generated constructor stub
   }

   public DateTime getStart() {
    return start;
  }

  public void setStart( DateTime start ) {
    this.start = start;
  }
}

The exception is as follows:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Invalid format: "11/23/2016" is malformed at "/23/2016" (through reference chain: com.firstfuel.analytics.bpu.translator.Bean["start"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:232)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:197)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1420)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:244)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2986)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2091)
at com.firstfuel.analytics.bpu.translator.VTModelPostProcessRequestTranslator.main(VTModelPostProcessRequestTranslator.java:125)

Caused by: java.lang.IllegalArgumentException: Invalid format: "11/23/2016" is malformed at "/23/2016"
at org.joda.time.format.DateTimeFormatter.parseMillis(DateTimeFormatter.java:754)
at org.joda.time.convert.StringConverter.getInstantMillis(StringConverter.java:65)
at org.joda.time.base.BaseDateTime.<init>(BaseDateTime.java:150)
at org.joda.time.DateTime.<init>(DateTime.java:265)
at com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer.deserialize(DateTimeDeserializer.java:48)
at com.fasterxml.jackson.datatype.joda.deser.DateTimeDeserializer.deserialize(DateTimeDeserializer.java:20)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:525)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:99)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:242)
... 4 more

This looks so simple but I am not sure why there is an issue. I can't change the date format to anything but "MM/dd/yyyy". I used Java Date and it works but I am not able to persist the data into database since the columns are mapped as Joda LocalDate in the entity object and when I convert Date to LocalDate I am having persistent issue. But lets not go there. If someone can help me with my above problem I will appreciate it a lot.


Answer:

I know that the question is already answered but I want to share with you an alternative way to solve this without so many lines of code. Just add the @JsonFormat annotation to the pojo property.

@JsonFormat( pattern="MM/dd/yyyy")
public DateTime start;

and to serialize also the time part

@JsonFormat(pattern = "dd-MM-yyyy hh:mm:ss")
public DateTime field = new DateTime();

You also need to register JodaModule to jackson to take advantage of it.

final ObjectMapper result = new ObjectMapper();  
result.registerModule(new JodaModule());

Mode details about this module at https://github.com/FasterXML/jackson-datatype-joda

Last note: Java8 SE includes a new date and time library, so Joda time library could be avoided (more details at http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html). In order to start using it, you need to register the module JavaTimeModule instead of JodaModule. It is included in 'jackson-datatype-jsr310'