Hot questions for Using Joda-Time in json

Question:

I have an object in which is nested a Joda DateTime object, and I'm having trouble serializing it and deserializing it.

When I serialize the full object that DateTime is nested in, the output looks vastly different than when I serialize the DateTime object directly, my results are below

new ObjectMapper.writeValueAsString(fullObjectWithDateTime) produces the following blob for the DateTime object:

{
    "dateTime": {
        "weekOfWeekyear": 34,
        "weekyear": 2019,
        "yearOfEra": 2019,
        "yearOfCentury": 19,
        "centuryOfEra": 20,
        "secondOfDay": 4530,
        "minuteOfDay": 75,
        "millisOfDay": 4530777,
        "monthOfYear": 8,
        "hourOfDay": 1,
        "minuteOfHour": 15,
        "secondOfMinute": 30,
        "millisOfSecond": 777,
        "year": 2019,
        "dayOfMonth": 21,
        "dayOfWeek": 3,
        "era": 1,
        "dayOfYear": 233,
        "millis": 1566350130777,
        "chronology": {
            "zone": {
                "fixed": true,
                "id": "UTC"
            }
        },
        "zone": {
            "fixed": true,
            "id": "UTC"
        },
        "afterNow": false,
        "beforeNow": true,
        "equalNow": false
    }
}

ObjectMapper.writeValueAsString(fullObjectWithDateTime.getDateTime())

produces:

"2019-08-21T01:15:30.777Z"

The error comes when I try to deserialize the object produced by ObjectMapper.writeValueAsString(fullObjectWithDateTime), where it says

'com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of org.joda.time.DateTime out of START_OBJECT token'

Answer:

You need to register JodaModule.

Example:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import org.joda.time.DateTime;

public class JsonApp {

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

        String json = mapper.writeValueAsString(new MyModel());
        System.out.println(json);
        MyModel model = mapper.readValue(json, MyModel.class);
        System.out.println(model);
    }
}

class MyModel {
    private DateTime now = DateTime.now();

    public DateTime getNow() {
        return now;
    }

    public void setNow(DateTime now) {
        this.now = now;
    }

    @Override
    public String toString() {
        return "MyModel{" +
                "now=" + now +
                '}';
    }
}

Above code prints:

{"now":"2019-09-06T21:58:34.917Z"}
MyModel{now=2019-09-06T21:58:34.917Z}

Question:

I would like to set global Jackson serialization setting for Local Date and use something like JavaTimeModule for java.time but for JodaTime's Local Date.

Does something like JavaTimeModule exist for Joda Time?


Answer:

Yes exists. Take a look at: JodaModule

Question:

I'm trying to create a simple REST service with JAX-RS (Jersey), without using Spring. And I'm using Joda as date fields in my entity.

To configure automatic json mapping, I create a JsonMapperProvider, where I add JodaModule:

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JsonMapperProvider implements ContextResolver<ObjectMapper> {

    final ObjectMapper objectMapper;

    public JsonMapperProvider() {
        objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JodaModule());
    }

    @Override
    public ObjectMapper getContext(Class<?> arg0) {
        return objectMapper;
    }

}

This is my Resource class:

@Path("users")
public class UserController {

    @Inject
    private UserService userService;

    @GET
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public User getUserById(@PathParam("id") Long id) {

        return userService.findById(id);
    }
}

And I'm using a "no web.xml" configuration, with this class:

@ApplicationPath("api")
public class RestApplication extends ResourceConfig {

}

But it doesn't work... the LocalDate field in User entity is always returned empty. The only workaround I found is to register all the components (including JacksonFeature class from jersey-media-json-jackson) in the ResourceConfig class, like this:

@ApplicationPath("api")
public class RestApplication extends ResourceConfig {

    public RestApplication() {
        super(
                UserController.class,
                JsonMapperProvider.class,
                JacksonFeature.class
        );
    }
}

Is there another solution to this problem? I'd rather not to register all my services and other stuff in this class manually...


Answer:

No, this is how it's supposed to work. You can also override the methods in javax.ws.rs.core.Application instead of extending ResourceConfig

Question:


Answer:

Use Days Class of Joda Library

Days days=Days.daysBetween(startDate.toLocalDate(), endDate.toLocalDate()).getDays();
int noOfDays=days.getDays();

As an alternative we can also ues Java 8 Date and Time API

public void days_between_two_dates_in_java_with_java8 () {

    LocalDate startDate = LocalDate.now().minusDays(1);
    LocalDate endDate = LocalDate.now();

    long days = Period.between(startDate, endDate).getDays();


    // or 

    long days2 = ChronoUnit.DAYS.between(startDate, endDate);

}

Question:

I am trying to serialize Java objects to JSON. One of my Java objects has a JodaTime LocalTime object as one of its fields.

A fair number of my Java objects also have various fields that are Collections that could be empty. I want to prevent the serialization of JSON that looks like this:

{id: 2348904, listOfThings: [], listOfStuff: [], nowASet: []}

In this scenario where those three Collections are empty, I would rather see this JSON:

{id: 2348904}

The correct way to do such a thing is to configure the ObjectMapper with the following line of code:

objectMapper.setSerializationInclusion(Include.NON_EMPTY);

This works just fine...until I hit that Java object with the LocalTime inside of it. That's when I get an actual java.lang.StackOverflowError.

It seems to be ping-ponging between JodaDateSerializerBase.isEmpty() and JsonSerializer.isEmpty(). I'm not sure how, though, because they don't call each other.

I managed to make a SSSSSSCCCCEEEE, or whatever the hell the acronym is, as follows:

package whatever.you.like;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import org.joda.time.LocalTime;
import org.junit.Test;

public class TestClass {
  public class JodaMapper extends ObjectMapper {
    private static final long serialVersionUID = 34785437895L;

    public JodaMapper() {
      registerModule(new JodaModule());
    }

    public boolean getWriteDatesAsTimestamps() {
      return getSerializationConfig().isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }

    public void setWriteDatesAsTimestamps(boolean state) {
      configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, state);
    }
  }

  private class Thing {
    private LocalTime localTime;

    public Thing() {}

    public void setLocalTime(LocalTime localTime) {
      this.localTime = localTime;
    }

    public LocalTime getLocalTime() {
      return localTime;
    }
  }

  @Test
  public void extendObjectMapperTest() throws JsonProcessingException {
    JodaMapper objectMapper = new JodaMapper();
    objectMapper.setWriteDatesAsTimestamps(false);
    objectMapper.setSerializationInclusion(Include.NON_EMPTY);
    Thing thing = new Thing();
    LocalTime localTime = new LocalTime(12389340L);
    thing.setLocalTime(localTime);
    System.out.println("Never manages to print this out: " + objectMapper.writeValueAsString(thing));
  }

  @Test
  public void configureObjectMapperTest() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new JodaModule());
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    objectMapper.setSerializationInclusion(Include.NON_EMPTY);
    Thing thing = new Thing();
    LocalTime localTime = new LocalTime(12389340L);
    thing.setLocalTime(localTime);
    System.out.println("Never manages to print this out: " + objectMapper.writeValueAsString(thing));
  }
}

I tried both extending the ObjectMapper and configuring the ObjectMapper, and I get the same error each time.

Dependencies:

Interestingly, you can find in that GitHub a unit test ("testLocalDateSer()") that claims to succeed using the Include.NON_EMPTY qualifier. I fail to see how it could possibly function.


Answer:

Upgrade to

  • FasterXML's Jackson 2.5.3
  • FasterXML's Jackson-DataType-Joda 2.5.3.

This works.

 @Test
  public void configureObjectMapperTest() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new JodaModule());
//    objectMapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false);
    objectMapper.setSerializationInclusion(Include.NON_EMPTY);
    Thing thing = new Thing();
    LocalTime localTime = new LocalTime(12389340L);
    thing.setLocalTime(localTime);
    System.out.println("Never manages to print this out: " + objectMapper.writeValueAsString(thing));
  }