我想将一个org.joda.time.DateTime作为json POST的消息体传递给Jersey端点。我的泽西使用MOXy。我已经创建了一个自定义XmlAdapter来实现这一点,但我不清楚如何连接这个适配器。我发现自定义适配器的示例在要发布的对象上使用注释,我不能这样做(我不能在DateTime类上放置注释,因为我无法操纵其源代码)。
我正在使用Jersey测试框架来测试它。
我的泽西终点:
@POST
@Path("/{memberId}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public ResultBean recordDate(@PathParam("memberId") Long memberId, DateTime dateTime) {
// TODO stuff happens
return new ResultBean(memberId, dateTime);
}
我的泽西测试:
public class FooEndpointImplTest extends JerseyTest {
@Test
public void testWithDate() {
Long memberId = 1L;
DateTime date = new DateTime();
Entity<DateTime> dateEntity = Entity.json(date);
ResultBean result = target(
"/" + memberId)
.request().post(dateEntity, ResultBean.class);
assertNotNull(result);
}
}
我的适配器:
package foo;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.joda.time.DateTime;
public class DateTimeAdapter extends XmlAdapter<String, DateTime> {
@Override
public DateTime unmarshal(String v) throws Exception {
Long millis = Long.parseLong(v);
return new DateTime(millis);
}
@Override
public String marshal(DateTime v) throws Exception {
return Long.toString(v.getMillis());
}
}
当我按原样运行此测试时,我收到500错误。
答案 0 :(得分:1)
首先要做的是:joda DateTime需要MessageBodyReader/Writer
。
如果没有一些调整,看起来像JSON序列化的DateTime的POST将无法工作。 Gson序列化的DateTime将如下所示:
{ "iMillis": 1414507195233,
"iChronology": {
"iBase": {
"iBase": {
...
},
"iParam": {
...
}}}}
如果您尝试使用Gson再次反序列化,Gson将失败,因为org.joda.time.Chronology
- &gt; iChronology
没有 default-non-arg构造函数,Gson需要对该对象进行反序列化。 Afaig,在使用任何标准反序列化器反序列化DateTime时,您将遇到此类问题。
所以我最终根据FasterXML/jackson-datatype-joda创建了MessageBodyReader / Writer
<强>阅读器:强>
// ...
import org.joda.time.DateTime;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
@Consumes(MediaType.APPLICATION_JSON)
public class JodaTimeBodyReader implements MessageBodyReader<DateTime> {
private static ObjectMapper mapper = new ObjectMapper();
public JodaTimeBodyReader() {
mapper.registerModule( new JodaModule());
}
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type == DateTime.class;
}
@Override
public DateTime readFrom(Class<DateTime> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
try {
return mapper.readValue(entityStream, DateTime.class);
} catch (Exception e) {
throw new ProcessingException("Error deserializing a org.joda.time.DateTime.", e);
}
}
}
<强>编剧:强>
// ...
import org.joda.time.DateTime;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class JodaTimeBodyWriter implements MessageBodyWriter<DateTime> {
private static ObjectMapper mapper = new ObjectMapper();
public JodaTimeBodyWriter() {
mapper.registerModule( new JodaModule());
}
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type == DateTime.class;
}
@Override
public long getSize(DateTime t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
// deprecated by JAX-RS 2.0 and ignored by Jersey runtime
return 0;
}
@Override
public void writeTo(DateTime t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
try {
entityStream.write( mapper.writeValueAsBytes(t));
} catch (Exception e) {
throw new ProcessingException("Error serializing a org.joda.time.DateTime to the output stream", e);
}
}
}
两者均在ResourceConfig中注册。
Writer将以1414507195233(yap,而非JSON)回复,如果您将此回发到您的资源,您将获得有效的DateTime。
如果您像上面的示例一样获得JSON,您现在可以升级Reader以解析iMillis的JSON并使用Long值。对于TimeZone,您也可以使用dateTime.withZone(...)
来设置值。
我使用的Maven依赖项:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.4.0</version>
</dependency>
泽西岛2.12
希望这在某种程度上有所帮助。
答案 1 :(得分:1)
而不是:
@POST
@Path("/{memberId}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public ResultBean recordDate(@PathParam("memberId") Long memberId, DateTime dateTime) {
// TODO stuff happens
return new ResultBean(memberId, dateTime);
}
你可以这样做:
@POST
@Path("/{memberId}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public ResultBean recordDate(@PathParam("memberId") Long memberId, DateTimeWrapper dateTimeWrapper) {
// TODO stuff happens
return new ResultBean(memberId, dateTime);
}
DateTimeWrapper
的样子。由于DateTime
对象现在是属性而非顶级,XmlAdapter
将适用。
@XmlAccessorType(XmlAccessType.FIELD)
public class DateTimeWrapper {
@XmlValue
@XmlJavaTypeAdapter(YourDateTimeAdapter.class)
private DateTime value;
}