Джерси + Джексон JSON формат даты формат - как изменить формат или использовать пользовательские JacksonJsonProvider
Я использую Jersey + Jackson для предоставления уровня сервиса REST JSON для моего приложения. Проблема заключается в том, что формат сериализации по умолчанию выглядит так:
"CreationDate":1292236718456
Сначала я думал, что это временная метка UNIX... но это слишком долго для этого. У моей клиентской JS-библиотеки есть проблемы, десериализирующие этот формат (он поддерживает кучу разных форматов дат, но не этот, я полагаю). Я хочу изменить формат, чтобы он мог потребляться моей библиотекой (например, по ISO). Как это сделать... Я нашел фрагмент кода, который мог бы помочь, но... где я могу это выразить, поскольку я не контролирую экземпляр Jackson serializer (Джерси)?
objectMapper.configure(
SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
Я также нашел этот код для пользовательского 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;
}
}
}
Ответы
Ответ 1
Для чего это стоит, это число является стандартной меткой времени Java (используется классами JDK); Unix хранит секунды, миллисекунды Java, поэтому он бит большего значения.
Я надеюсь, что есть некоторые документы о том, как вводить ObjectMapper в Джерси (он должен следовать обычному способу ввода объекта). Но в качестве альтернативы вы можете переопределить JacksonJaxRsProvider, чтобы указать/настроить ObjectMapper и зарегистрировать это; это то, что делает сам Джерси, и есть несколько способов сделать это.
Ответ 2
Мне удалось сделать это в Restaasy "JAX-RS way", поэтому он должен работать на любой совместимой реализации, такой как Jersey (недавно успешно протестированный на JEE7-сервере Wildfly 8), просто потребовалось несколько изменений в части Джексона, потому что они изменилось несколько 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;
}
}
Таким образом, вся операция, выполняемая через jackson, предоставляется ObjectMapper по вашему выбору.
EDIT: Недавно я выяснил, что с помощью RestEasy 2.0.1 (и, следовательно, Jackson 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>
Ответ 3
Чтобы настроить собственный ObjectMapper, вам нужно ввести свой собственный класс, который реализует ContextResolver <ObjectMapper>
Точно, как получить майку, чтобы забрать ее, будет зависеть от вашего IOC (spring, guice). Я использую spring, и мой класс выглядит примерно так:
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 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;
}
}
Ответ 4
У меня была такая же проблема (с использованием Jersey + Jackson + Json), клиент отправил дату, но она была изменена на сервере, когда данные были сопоставлены с объектом.
Я воспользовался другим подходом, чтобы решить эту проблему, прочитав эту ссылку: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html, когда я понял, что полученная дата была TimeStamp ( то же, что и у Адрина в его вопросе: "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;
}
}
Надеюсь, это тоже поможет вам.
Ответ 5
Перезапишите 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;
}
}
Ответ 6
Если вы решили работать с объектами Joda DateTime на своем сервере и хотите сериализоваться на ISO8601, вы можете использовать Jackson 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;
}
}
Дополнительная информация доступна на сайте Джерси.
Ответ 7
json-io (https://github.com/jdereg/json-io) - это полная библиотека Java для JSON-сериализации. Когда вы используете его для записи строки JSON, вы можете указать, как отформатировать даты. По умолчанию даты выписываются так долго (как указано выше, с миллисекунд с 1 января 1970 г.). Тем не менее, вы можете дать ему формат String или Java DateFormatter и указать даты, записанные в любом формате.