Jersey + Jackson JSON сериализация формата даты - как изменить формат или использовать пользовательский JacksonJsonProvider - PullRequest
37 голосов
/ 13 декабря 2010

Я использую Джерси + Джексон, чтобы обеспечить уровень служб REST JSON для моего приложения.У меня проблема в том, что формат сериализации даты по умолчанию выглядит так:

"CreationDate":1292236718456

Сначала я подумал, что это метка времени UNIX ... но это слишком долго для этого.У моей JS-библиотеки на стороне клиента есть проблемы с десериализацией этого формата (она поддерживает кучу разных форматов дат, но я полагаю, не этот).Я хочу изменить формат, чтобы он мог использоваться моей библиотекой (например, ISO).Как мне это сделать ... Я нашел кусок кода, который мог бы помочь, но ... куда я могу его поместить, поскольку я не контролирую создание экземпляров сериализатора Джексона (Джерси это делает)?Я также нашел этот код для пользовательского JacksonJsonProvider - вопрос в том ... как мне заставить его использовать все мои классы POJO?

@Provider
public class MessageBodyWriterJSON extends JacksonJsonProvider {

    private static final String DF = "yyyy-MM-dd’T'HH:mm:ss.SSSZ";

    @Override
    public boolean isWriteable(Class arg0, Type arg1, Annotation[] arg2,
            MediaType arg3) {
        return super.isWriteable(arg0, arg1, arg2,
                arg3);
    }
    @Override
    public void writeTo(Object target, Class arg1, Type arg2, Annotation[] arg3,
            MediaType arg4, MultivaluedMap arg5, OutputStream outputStream)
            throws IOException, WebApplicationException {
            SimpleDateFormat sdf=new SimpleDateFormat(DF);

        ObjectMapper om = new ObjectMapper();
        om.getDeserializationConfig().setDateFormat(sdf);
        om.getSerializationConfig().setDateFormat(sdf);
        try {
            om.writeValue(outputStream, target);
        } catch (JsonGenerationException e) {
            throw e;
        } catch (JsonMappingException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        }
    }
}

Ответы [ 8 ]

31 голосов
/ 08 марта 2011

Мне удалось сделать это в Resteasy «способом JAX-RS», поэтому он должен работать на каждой совместимой реализации, например на Джерси (недавно успешно протестированной на сервере Wildfly 8 на JEE7, просто потребовалось несколько изменений в части Джексона, потому что они изменил несколько API).

Вы должны определить ContextResolver (проверьте, что Produces содержит правильный тип контента):

import javax.ws.rs.ext.ContextResolver;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.DeserializationConfig;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.Produces; 
import java.text.SimpleDateFormat;
@Provider
@Produces("application/json")
public class JacksonConfigurator implements ContextResolver<ObjectMapper> {

    private ObjectMapper mapper = new ObjectMapper();

    public JacksonConfigurator() {
        SerializationConfig serConfig = mapper.getSerializationConfig();
        serConfig.setDateFormat(new SimpleDateFormat(<my format>));
        DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
        deserializationConfig.setDateFormat(new SimpleDateFormat(<my format>));
        mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

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

}

Затем вы должны вернуть только что созданный класс в ваш javax.ws.rs.core.Application в getClasses

import javax.ws.rs.core.Application;
public class RestApplication extends Application {

     @Override
     public Set<Class<?>> getClasses() {
         Set<Class<?>> classes = new HashSet<Class<?>>();
         // your classes here
         classes.add(JacksonConfigurator.class);
         return classes;
      }

}

таким образом, все операции, выполняемые через Джексона, получают ObjectMapper на ваш выбор.

РЕДАКТИРОВАТЬ: Я недавно узнал за свой счет, что при использовании RestEasy 2.0.1 (и, следовательно, Джексон 1.5.3) есть странное поведение, если вы решите расширить JacksonConfigurator для добавления пользовательских сопоставлений.

import javax.ws.rs.core.MediaType;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class MyJacksonConfigurator extends JacksonConfigurator

Если вы просто делаете это (и, конечно, помещаете расширенный класс в RestApplication), используется преобразователь родительского класса, то есть вы теряете пользовательские сопоставления. Чтобы это работало правильно, я должен был сделать что-то, что мне кажется бесполезным:

public class MyJacksonConfigurator extends JacksonConfigurator implements ContextResolver<ObjectMapper> 
15 голосов
/ 29 января 2011

Чтобы настроить свой собственный ObjectMapper, вам нужно добавить свой собственный класс, который реализует ContextResolver

Как именно подобрать майку, зависит от вашего МОК (весна, хитрость). Я использую весну, и мой класс выглядит примерно так:

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig.Feature;
import org.codehaus.jackson.map.deser.CustomDeserializerFactory;
import org.codehaus.jackson.map.deser.StdDeserializerProvider;
import org.codehaus.jackson.map.ser.CustomSerializerFactory;
import org.springframework.stereotype.Component;

// tell spring to look for this.
@Component
// tell spring it's a provider (type is determined by the implements)
@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
    @Override
    public ObjectMapper getContext(Class<?> type) {
        // create the objectMapper.
        ObjectMapper objectMapper = new ObjectMapper();
        // configure the object mapper here, eg.
           objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }
}
6 голосов
/ 13 декабря 2010

Для чего стоит, это число является стандартной меткой времени Java (используется классами JDK); Unix хранит секунды, миллисекунды Java, поэтому это немного большее значение.

Я надеюсь, что есть некоторые документы о том, как внедрить ObjectMapper в Джерси (он должен следовать обычному способу введения предоставленного объекта). Но в качестве альтернативы вы можете переопределить JacksonJaxRsProvider, чтобы указать / настроить ObjectMapper и зарегистрировать это; это то, что делает сам Джерси, и есть несколько способов сделать это.

1 голос
/ 29 июня 2018

Ниже код работал для меня - JAX-RS 1.1, Jersy 1.8

import java.text.SimpleDateFormat;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;


@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JsonProvider extends JacksonJaxbJsonProvider {
  private static final ObjectMapper objectMapper = new ObjectMapper();
  static {
    // allow only non-null fields to be serialized
    objectMapper.getSerializationConfig().setSerializationInclusion(Inclusion.NON_NULL);

    SerializationConfig serConfig = objectMapper.getSerializationConfig();
    serConfig.setDateFormat(new SimpleDateFormat(<your date format>));
    DeserializationConfig deserializationConfig = objectMapper.getDeserializationConfig();
    deserializationConfig.setDateFormat(new SimpleDateFormat(<your date format>));
    objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);

  }

  public JsonProvider() {
    super.setMapper(objectMapper);
  }
}
1 голос
/ 29 августа 2014

У меня была та же проблема (с использованием Джерси + Джексон + Джсон), клиент отправлял дату, но она изменялась на сервере, когда данные отображались в объект.

Я воспользовался другим подходом для решения этой проблемы, прочитав эту ссылку: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html,, когда понял, что полученная дата была меткой времени (такая же, как у Адрина в его вопросе: "creationDate":1292236718456)

В своем классе VO я добавил эту аннотацию к атрибуту @XmlJavaTypeAdapter, а также реализовал внутренний класс, который расширил XmlAdapter:

@XmlRootElement
public class MyClassVO {
   ...
   @XmlJavaTypeAdapter(DateFormatterAdapter.class) 
   Date creationDate;
   ...

   private static class DateFormatterAdapter extends XmlAdapter<String, Date> {
      @Override
      public Date unmarshal(final String v) throws Exception {
         Timestamp stamp = new Timestamp(new Long(v));
         Date date = new Date(stamp.getTime());
         return date;
      }
}

Надеюсь, это может помочь и вам.

1 голос
/ 14 октября 2013

Если вы решили работать с объектами Joda DateTime на вашем сервере и хотите сериализовать их в ISO8601, вы можете использовать JodaModule Джексона .Вы можете зарегистрировать провайдера Джерси следующим образом:

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

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

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {

  final ObjectMapper objectMapper;

  public MyObjectMapperProvider() {
    objectMapper = new ObjectMapper();
    /* Register JodaModule to handle Joda DateTime Objects. */
    objectMapper.registerModule(new JodaModule());
    /* We want dates to be treated as ISO8601 not timestamps. */
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
  }

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

Более подробная информация доступна на сайте Джерси .

0 голосов
/ 24 февраля 2014

json-io (https://github.com/jdereg/json-io) - это полная библиотека сериализации Java to / from JSON. Используя ее для записи строки JSON, вы можете установить способ форматирования дат .даты записываются так долго (как указано выше, что составляет миллисекунды с 1 января 1970 года). Однако вы можете присвоить им формат String или Java DateFormatter и записать даты в любом формате, который вы пожелаете.

0 голосов
/ 05 апреля 2012

Переписать MessageBodyWriterJSON с этим

import javax.ws.rs.core.MediaType; 
import javax.ws.rs.ext.Provider; 

import org.codehaus.jackson.jaxrs.JacksonJsonProvider; 
import org.codehaus.jackson.map.ObjectMapper; 
import org.codehaus.jackson.map.SerializationConfig; 

@Provider 
public class MessageBodyWriterJSON extends JacksonJsonProvider { 
            public MessageBodyWriterJSON (){ 
            } 

        @Override 
            public ObjectMapper locateMapper(Class<?> type, MediaType mediaType) 
        { 
        ObjectMapper mapper = super.locateMapper(type, mediaType); 
        //DateTime in ISO format "2012-04-07T17:00:00.000+0000" instead of 'long' format 
            mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); 
            return mapper; 
        } 
}
...