Search code examples
javajsonjacksonjackson-modulesjackson2

jackson make two json attributes of one java attribute


I have a little bit of a comfort problem using jackson in a rest api. I am using jackson to serialize object that have attributes of any type in java.time like:

public class DomainObject{
   public LocalDateTime start;
}

I can use jackson to produce something like this:

{
    start: '2017-12-31 17:35:22'
}

And i can use it to produce something like this:

{
    start: 102394580192345 //milliseconds
}

But i would like to have both, the milliseconds to workwith in JS and the String representation for Users who use the rest-api purely without js-frontend. (Mostly me, for debugging)

So is there any way, to make jackson produce the following?

{
    start: 102394580192345 //milliseconds
    startString: '2017-12-31 17:35:22'
}

Solution

  • You can write a custom Jackson Serializer which then has to be registered in the application. Afterwards every occurence of that dataType will be serialized in this way. Even if the datatype to serialize is inside another dataType. ( just like your example )

    note: Bear with me not writing it for LocalDateTime since I wrote it for ZonedDateTime in my application. It should be no hard task for you to rewrite it to your needs.

    public class ZonedDateTimeSerializer extends JsonSerializer<ZonedDateTime>
    {
    
      @Override
      public void serialize( ZonedDateTime value, JsonGenerator gen, SerializerProvider serializers ) throws IOException
      {
        gen.writeStartObject();
        gen.writeFieldName( "timestamp" );
        gen.writeString( Long.toString( value.toInstant().toEpochMilli() ) );
        gen.writeFieldName( "offset" );
        gen.writeString( value.getOffset().toString() );
        gen.writeFieldName( "zone" );
        gen.writeString( value.getZone().toString() );
        gen.writeFieldName( "ts" );
        gen.writeString( StringUtils.convertZonedDateTimeToISO8601String( value ) );
        gen.writeEndObject();
      }
    
    }
    

    It is then registered for the ObjectMapper of Jackson like this:

        objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule( "MyModule" );
        module.addSerializer( ZonedDateTime.class, new ZonedDateTimeSerializer() );
        objectMapper.registerModule( module );
    

    While in this process I would suggest to create A Producer ( CDI ) which will always return a objectMapper with this serializer added by default. But I will leave that task up to you to research ;)