Wednesday, 26 September 2018

JSON Date / Time Formatting

The default behaviour when serialising json from a Java Date object is to break it down to its constituent parts eg,

"startDate" : {
    "year" : 2014,
    "month" : "MARCH",
    "dayOfMonth" : 1,
    "dayOfWeek" : "FRIDAY",
    "dayOfYear" : 1,
    "monthValue" : 1,
    "hour" : 2,
    "minute" : 2,
    "second" : 0,
    "nano" : 0,
    "chronology" : {
      "id" : "ISO",
      "calendarType" : "iso8601"
    }
  }

Spring Boot 2

Spring boot already contains the dependencies,

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>

and it'll wire the jsr310 dependency automatically for any ObjectMapper that is @Autowired.  All that is necessary then is to add the property,

    spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false

Standalone

For standalone code the dependencies above will need to be added.  In the code when the object mapper is created the following is necessary,

    final ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    objectMapper.registerModule(new JavaTimeModule());


Deserializer

Of course if you already have a file that is serialized like this it is too late.  In that case you can transform the file using some deserializers.  Below are two deserializers for LocalDate and LocalDateTime to allow you to reconstruct your object.




  public class LocalDateDeserializer extends StdDeserializer<LocalDate>
  {
      public LocalDateDeserializer()
      {
          this(null);
      }

      public LocalDateDeserializer(final Class<LocalDate> t)
      {
          super(t);
      }

      @Override
      public LocalDate deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException
      {
          JsonNode node = p.getCodec().readTree(p);
          return LocalDate.of(node.get("year").intValue(),
                              node.get("monthValue").intValue(),
                              node.get("dayOfMonth").intValue());
      }
  }

  public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime>
  {
      public LocalDateTimeDeserializer()
      {
          this(null);
      }

      public LocalDateTimeDeserializer(final Class<LocalDateTime> t)
      {
          super(t);
      }

      @Override
      public LocalDateTime deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException
      {
          JsonNode node = p.getCodec().readTree(p);
          return LocalDateTime.of(node.get("year").intValue(),
                                  node.get("monthValue").intValue(),
                                  node.get("dayOfMonth").intValue(),
                                  node.get("hour").intValue(),
                                  node.get("minute").intValue(),
                                  node.get("second").intValue(),
                                  node.get("nano").intValue()
                                  );
      }
  }

These allow you to write code to read in your existing badly formatted json like so,

  final ObjectMapper mapper = new ObjectMapper();

  SimpleModule module = new SimpleModule();
  module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
  module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
  mapper.registerModule(module);
  final MyObject myobj = mapper.readValue(new FileInputStream("<file to read in>"), MyObject.class);

  final ObjectMapper dateTimeMapper = new ObjectMapper();
  dateTimeMapper.registerModule(new JavaTimeModule();  dateTimeMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
  final FileOutputStream os = new FileOutputStream("<file to write out");
  dateTimeMapper.writeValue(os, myobj);
  os.flush();

  os.close();

No comments:

Post a Comment