Search code examples
javajsonstack-overflowjodatimefasterxml

JodaTime LocalTime to JSON - Actual Stack Overflow


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.


Solution

  • 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));
      }