I am using Jersey + Jackson to provide REST JSON services layer for my application. The problem I have is that the default Date serialization format looks like that:
我使用Jersey + Jackson为我的应用程序提供REST JSON服务层。我的问题是默认的日期序列化格式是这样的:
"CreationDate":1292236718456
At first I thought it is a UNIX timestamp... but it is too long for that. My client-side JS library has problems deserializing this format (it supports a bunch of different date formats but not this one I suppose). I want to change the format so that it can be consumable by my library (to ISO for example). How do I do that... I found a piece of code that could help but... where do I put it as I don't control the Jackson serializer instantiation (Jersey does)?
起初我以为是UNIX时间戳……但时间太长了。我的客户端JS库有反序列化这种格式的问题(它支持一大堆不同的日期格式,但我认为不是这样)。我想改变格式,以便我的库(例如ISO)可以使用。我该怎么做…我找到了一段可以帮助但…我应该把它放在哪里,因为我不控制Jackson序列化器实例化(Jersey does)?
objectMapper.configure(
SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
I also found this code for custom JacksonJsonProvider
- the question is .. how do I make all my POJO classes use it?
我还发现了这个定制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;
}
}
}
7 个解决方案
#1
6
For what it's worth, that number is standard Java timestamp (used by JDK classes); Unix stores seconds, Java milliseconds, which is why it's bit larger value.
对于它的价值,这个数字是标准的Java时间戳(由JDK类使用);Unix存储秒,Java毫秒,这就是为什么它的值更大。
I would hope there are some documents as to how to inject ObjectMapper into Jersey (it should follow the usual way to inject provided object). But alternatively you could override JacksonJaxRsProvider to specify/configure ObjectMapper and register that; this is what Jersey itself does, and there are multiple ways to do it.
我希望有一些关于如何将ObjectMapper注入到Jersey的文档(它应该按照通常的方式注入所提供的对象)。但是,您也可以重写JacksonJaxRsProvider来指定/配置ObjectMapper并注册它;这就是Jersey本身所做的,有多种方法可以做到。
#2
30
I managed to do it in Resteasy "the JAX-RS way", so it should work on every compliant implementation like Jersey (recently successfully tested on JEE7 server Wildfly 8, it just required a few changes to the Jackson part because they changed a few APIs).
我在Resteasy“JAX-RS way”中做到了这一点,所以它应该在每个兼容的实现上工作,比如Jersey(最近在JEE7服务器Wildfly 8上成功地测试了它,它只需要对Jackson部分做一些更改,因为它们改变了一些api)。
You must define a ContextResolver (check that Produces contains the correct content-type):
您必须定义一个上下文解决程序(检查生成包含正确的内容类型):
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;
}
}
Then you must return the newly created class in your javax.ws.rs.core.Application's getClasses
然后,必须将新创建的类返回到javax.ws.r .core中。应用程序的getclass
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;
}
}
this way all operation made through jackson are given the ObjectMapper of your choice.
通过这种方式,所有通过jackson的操作都得到了您所选择的ObjectMapper。
EDIT: I recently found out at my expenses that using RestEasy 2.0.1 (and thus Jackson 1.5.3) there is a strange behaviour if you decide to extend the JacksonConfigurator to add custom mappings.
编辑:我最近发现使用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
If you just do like this (and of course put the extended class in RestApplication) the mapper of the parent class is used, that is you lose the custom mappings. To make it correctly work I had to do something that seems useless to me otherwise:
如果您这样做(当然是在RestApplication中放置扩展类),则使用父类的映射器,即丢失自定义映射。为了让它正确地工作,我不得不做一些对我来说毫无用处的事情:
public class MyJacksonConfigurator extends JacksonConfigurator implements ContextResolver<ObjectMapper>
#3
14
To configure your own ObjectMapper, you need to inject your own class that implements ContextResolver<ObjectMapper>
要配置您自己的ObjectMapper,您需要注入自己的类来实现上下文解析器
Exactly how to get jersey to pick this up will kind of depend on your IOC (spring, guice). I use spring, and my class looks something like this:
确切地说,如何让泽西拿起这个球将取决于你的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;
}
}
#4
1
I had the same problem (using Jersey+Jackson+Json), the client was sending a date, but it was being changed in the server when the data was mapped into the object.
我有同样的问题(使用Jersey+Jackson+Json),客户端正在发送一个日期,但是当数据被映射到对象中时,服务器正在更改。
I followed other approach to solve this, by reading this link: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html, when I realized that the date received was a TimeStamp (the same as Adrin in his question: "creationDate":1292236718456
)
我遵循其他方法来解决这个问题,通过阅读这个链接:http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret- tools .html,当我意识到收到的日期是一个时间戳(在他的问题:“creationDate”:1292236718456)
In my VO class I added this annotation to the attribute @XmlJavaTypeAdapter
and also implemented an inner class wich extended XmlAdapter
:
在我的VO类中,我在属性@XmlJavaTypeAdapter中添加了这个注释,并实现了一个内部类wich扩展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;
}
}
I hope it could help to you also.
我希望这对你也有帮助。
#5
0
If you choose to work with Joda DateTime objects on your server and want to serialize to ISO8601 you could use Jackson's JodaModule. You can register a Jersey Provider as follows:
如果您选择在服务器上使用Joda DateTime对象,并希望序列化到ISO8601,您可以使用Jackson的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;
}
}
More information available on Jersey's website.
更多关于泽西岛网站的信息。
#6
-1
Re-write the MessageBodyWriterJSON with this
用这个重写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;
}
}
#7
-1
json-io (https://github.com/jdereg/json-io) is a complete Java to / from JSON serialization library. When using it to write the JSON string, you can set how dates are formatted. By default, dates are written out as long (as above, which is milliseconds since Jan 1, 1970). However, you can give it a format String or Java DateFormatter and have the dates written in whatever format you wish.
JSON -io (https://github.com/j/json -io)是一个完整的Java到/从JSON序列化库。当使用它来编写JSON字符串时,您可以设置如何格式化日期。默认情况下,日期被写得很长(如上所述,自1970年1月1日以来的毫秒数)。但是,您可以给它一个格式字符串或Java DateFormatter,并以您希望的格式编写日期。
#1
6
For what it's worth, that number is standard Java timestamp (used by JDK classes); Unix stores seconds, Java milliseconds, which is why it's bit larger value.
对于它的价值,这个数字是标准的Java时间戳(由JDK类使用);Unix存储秒,Java毫秒,这就是为什么它的值更大。
I would hope there are some documents as to how to inject ObjectMapper into Jersey (it should follow the usual way to inject provided object). But alternatively you could override JacksonJaxRsProvider to specify/configure ObjectMapper and register that; this is what Jersey itself does, and there are multiple ways to do it.
我希望有一些关于如何将ObjectMapper注入到Jersey的文档(它应该按照通常的方式注入所提供的对象)。但是,您也可以重写JacksonJaxRsProvider来指定/配置ObjectMapper并注册它;这就是Jersey本身所做的,有多种方法可以做到。
#2
30
I managed to do it in Resteasy "the JAX-RS way", so it should work on every compliant implementation like Jersey (recently successfully tested on JEE7 server Wildfly 8, it just required a few changes to the Jackson part because they changed a few APIs).
我在Resteasy“JAX-RS way”中做到了这一点,所以它应该在每个兼容的实现上工作,比如Jersey(最近在JEE7服务器Wildfly 8上成功地测试了它,它只需要对Jackson部分做一些更改,因为它们改变了一些api)。
You must define a ContextResolver (check that Produces contains the correct content-type):
您必须定义一个上下文解决程序(检查生成包含正确的内容类型):
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;
}
}
Then you must return the newly created class in your javax.ws.rs.core.Application's getClasses
然后,必须将新创建的类返回到javax.ws.r .core中。应用程序的getclass
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;
}
}
this way all operation made through jackson are given the ObjectMapper of your choice.
通过这种方式,所有通过jackson的操作都得到了您所选择的ObjectMapper。
EDIT: I recently found out at my expenses that using RestEasy 2.0.1 (and thus Jackson 1.5.3) there is a strange behaviour if you decide to extend the JacksonConfigurator to add custom mappings.
编辑:我最近发现使用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
If you just do like this (and of course put the extended class in RestApplication) the mapper of the parent class is used, that is you lose the custom mappings. To make it correctly work I had to do something that seems useless to me otherwise:
如果您这样做(当然是在RestApplication中放置扩展类),则使用父类的映射器,即丢失自定义映射。为了让它正确地工作,我不得不做一些对我来说毫无用处的事情:
public class MyJacksonConfigurator extends JacksonConfigurator implements ContextResolver<ObjectMapper>
#3
14
To configure your own ObjectMapper, you need to inject your own class that implements ContextResolver<ObjectMapper>
要配置您自己的ObjectMapper,您需要注入自己的类来实现上下文解析器
Exactly how to get jersey to pick this up will kind of depend on your IOC (spring, guice). I use spring, and my class looks something like this:
确切地说,如何让泽西拿起这个球将取决于你的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;
}
}
#4
1
I had the same problem (using Jersey+Jackson+Json), the client was sending a date, but it was being changed in the server when the data was mapped into the object.
我有同样的问题(使用Jersey+Jackson+Json),客户端正在发送一个日期,但是当数据被映射到对象中时,服务器正在更改。
I followed other approach to solve this, by reading this link: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html, when I realized that the date received was a TimeStamp (the same as Adrin in his question: "creationDate":1292236718456
)
我遵循其他方法来解决这个问题,通过阅读这个链接:http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret- tools .html,当我意识到收到的日期是一个时间戳(在他的问题:“creationDate”:1292236718456)
In my VO class I added this annotation to the attribute @XmlJavaTypeAdapter
and also implemented an inner class wich extended XmlAdapter
:
在我的VO类中,我在属性@XmlJavaTypeAdapter中添加了这个注释,并实现了一个内部类wich扩展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;
}
}
I hope it could help to you also.
我希望这对你也有帮助。
#5
0
If you choose to work with Joda DateTime objects on your server and want to serialize to ISO8601 you could use Jackson's JodaModule. You can register a Jersey Provider as follows:
如果您选择在服务器上使用Joda DateTime对象,并希望序列化到ISO8601,您可以使用Jackson的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;
}
}
More information available on Jersey's website.
更多关于泽西岛网站的信息。
#6
-1
Re-write the MessageBodyWriterJSON with this
用这个重写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;
}
}
#7
-1
json-io (https://github.com/jdereg/json-io) is a complete Java to / from JSON serialization library. When using it to write the JSON string, you can set how dates are formatted. By default, dates are written out as long (as above, which is milliseconds since Jan 1, 1970). However, you can give it a format String or Java DateFormatter and have the dates written in whatever format you wish.
JSON -io (https://github.com/j/json -io)是一个完整的Java到/从JSON序列化库。当使用它来编写JSON字符串时,您可以设置如何格式化日期。默认情况下,日期被写得很长(如上所述,自1970年1月1日以来的毫秒数)。但是,您可以给它一个格式字符串或Java DateFormatter,并以您希望的格式编写日期。