我正在使用Jersey + Jackson为我的应用程序提供REST JSON服务层。我遇到的问题是默认的日期序列化格式如下所示:
"CreationDate":1292236718456
起初我以为它是一个UNIX时间戳......但它太长了。我的客户端JS库在反序列化这种格式时遇到了问题(它支持一堆不同的日期格式,但我认为不支持这种格式)。我想更改格式,以便我的库(例如ISO)可以使用它。我该怎么做...我发现了一段代码可以提供帮助,但是......我把它放在哪里,因为我不控制杰克逊序列化器实例化(泽西岛)?
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;
}
}
}
答案 0 :(得分:31)
我设法在Resteasy中使用“JAX-RS方式”,所以它应该适用于每个兼容的实现,如Jersey(最近在JEE7服务器Wildfly 8上成功测试,它只需要对Jackson部分进行一些更改,因为它们改变了一些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中返回新创建的类</ p>
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。
编辑:我最近发现,如果您决定扩展JacksonConfigurator以添加自定义映射,那么使用RestEasy 2.0.1(以及Jackson 1.5.3)会有一种奇怪的行为。
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>
答案 1 :(得分:15)
要配置自己的ObjectMapper,需要注入自己的实现ContextResolver的类&lt; ObjectMapper&gt;
究竟如何让球衣接受它将取决于你的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'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;
}
}
答案 2 :(得分:6)
对于它的价值,该数字是标准的Java时间戳(由JDK类使用); Unix存储秒,Java毫秒,这就是为什么它的值更大。
我希望有一些关于如何将ObjectMapper注入Jersey的文档(它应该按照通常的方式注入提供的对象)。但另外你可以覆盖JacksonJaxRsProvider来指定/配置ObjectMapper并注册它;这就是泽西岛本身所做的,并且有多种方法可以做到。
答案 3 :(得分:1)
如果您选择在服务器上使用Joda DateTime对象并想要序列化为ISO8601,则可以使用Jackson's JodaModule。您可以按如下方式注册Jersey提供商:
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;
}
}
有关Jersey's website的更多信息。
答案 4 :(得分:1)
我遇到了同样的问题(使用Jersey + Jackson + Json),客户端发送了一个日期,但是当数据映射到对象时,它在服务器中被更改了。
当我意识到收到的日期是TimeStamp(与他的问题中的Adrin相同:"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 :(得分:1)
以下代码对我有用-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);
}
}
答案 6 :(得分:-1)
使用此
重写MessageBodyWriter JSONimport 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;
}
}
答案 7 :(得分:-1)
json-io(https://github.com/jdereg/json-io)是一个完整的Java / JSON序列化库。使用它来编写JSON字符串时,您可以设置 日期的格式。默认情况下,日期写出的时间很长(如上所述,自1970年1月1日起为毫秒)。但是,您可以为其指定格式String或Java DateFormatter,并以您希望的任何格式编写日期。