Search code examples
javaspringspring-mvcjacksonlightadmin

Change Jackson date formatting setting in spring data rest webmvc


Lightadmin for timestamp fields such as:

@Temporal(TemporalType.TIMESTAMP)
@Column(name="started_at")
Date startedAt;

does not format them but shows them as the number of milliseconds since the epoch, e.g. 1398940456150.

When you enter a Lightadmin edit page e.g. http://localhost:8080/admin/domain/user/1/edit the values which the form is actually populated with are received in another request - http://localhost:8080/admin/rest/user/1/unit/formView?_=1401699535260, which returns JSON with:

...
"startedAt" : {
    "name" : "startedAt",
    "title" : "started at timestamp",
    "value" : 1398940456150,
    "type" : "DATE",
    "persistable" : true,
    "primaryKey" : false
}
...

The task is to change 1398940456150 to e.g. 01.05.2014 10:34:16.

According to my investigation, org.lightadmin.core.rest.DynamicRepositoryRestController.entity() is the entry point of such requests, the code that is responsible for generating JSON is inside: org.springframework.data.rest.webmvc.RepositoryAwareMappingHttpMessageConverter.writeInternal():

try {
  mapper.writeValue(jsonGenerator, object);
} catch(IOException ex) {
  throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}

mapper is an instance of org.codehaus.jackson.map.ObjectMapper.ObjectMapper, initialized with defaults. If it were possible to add these two lines:

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.getSerializationConfig().setDateFormat(df);

it would do the job, the question is how this can be done?


Solution

  • I posted this fix on Github - but here it is:

    I fixed this issue by changing the class DomainTypeResourceModule in the lightadmin code. Here is the updated source code of the class. There may be a better way to fix it - but this was the least intrusive way and it covered both, serialize and deserialize.

    package org.lightadmin.core.rest;
    
    import java.io.IOException;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.codehaus.jackson.JsonGenerator;
    import org.codehaus.jackson.JsonParser;
    import org.codehaus.jackson.JsonProcessingException;
    import org.codehaus.jackson.Version;
    import org.codehaus.jackson.map.DeserializationContext;
    import org.codehaus.jackson.map.SerializerProvider;
    import org.codehaus.jackson.map.deser.std.StdDeserializer;
    import org.codehaus.jackson.map.module.SimpleDeserializers;
    import org.codehaus.jackson.map.module.SimpleModule;
    import org.codehaus.jackson.map.module.SimpleSerializers;
    import org.codehaus.jackson.map.ser.std.SerializerBase;
    import org.springframework.hateoas.Resource;
    
    public class DomainTypeResourceModule extends SimpleModule {
    
        private final DomainTypeToResourceConverter domainTypeToResourceConverter;
    
        public DomainTypeResourceModule(final DomainTypeToResourceConverter domainTypeToResourceConverter) {
            super("DomainTypeResourceModule", Version.unknownVersion());
    
            this.domainTypeToResourceConverter = domainTypeToResourceConverter;
        }
    
        @Override
        public void setupModule(final SetupContext context) {
            SimpleSerializers serializers = new SimpleSerializers();
            serializers.addSerializer(DomainTypeResource.class, new DomainTypeResourceSerializer());
            serializers.addSerializer(Date.class, new JsonDateSerializer());
    
            SimpleDeserializers deserializers = new SimpleDeserializers();
            deserializers.addDeserializer(Date.class, new JsonDateDeserializer());
    
            context.addDeserializers(deserializers);
            context.addSerializers(serializers);
        }
    
        private class DomainTypeResourceSerializer extends SerializerBase<DomainTypeResource> {
    
            protected DomainTypeResourceSerializer() {
                super(DomainTypeResource.class);
            }
    
            @Override
            public void serialize(DomainTypeResource value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
                if (null == value) {
                    provider.defaultSerializeNull(jgen);
                    return;
                }
    
                final Resource resource = domainTypeToResourceConverter.convert(value.getResource(), value.getConfigurationUnitType(), value.getFieldMetadatas());
    
                jgen.writeObject(resource);
            }
        }
    
        private class JsonDateSerializer extends SerializerBase<Date> {
    
            protected JsonDateSerializer() {
                super(Date.class);
            }
    
            @Override
            public void serialize(Date date, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
    
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                String formattedDate = date == null ? "" : dateFormat.format(date);
    
                gen.writeString(formattedDate);
            }
    
        }
    
        private class JsonDateDeserializer extends StdDeserializer<Date> {
    
            protected JsonDateDeserializer() {
                super(Date.class);
            }
    
            @Override
            public Date deserialize(JsonParser json, DeserializationContext context) throws IOException, JsonProcessingException {
    
                try {
                    if(json.getText() != null && !"".equals(json.getText().trim())) {
                        try {
                            return new Date(Long.parseLong(json.getText()));
                        }
                        catch(NumberFormatException nex){
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                            return dateFormat.parse(json.getText());
                        }
                    }
                    else return null;
                }
                catch (ParseException e){
                    return null;
                }
            }
    
        }
    
    }