json - 如何将 Joda DateTime 对象作为 HTTP POST 的消息正文传递到 Jersey REST 端点?

标签 json jaxb jersey jodatime moxy

我想将 org.joda.time.DateTime 作为 json POST 的消息正文传递到 Jersey 端点。我的 Jersey 使用 MOXy。我创建了一个自定义 XmlAdapter 来执行此操作,但我不清楚如何连接此适配器。我找到的自定义适配器的示例在要发布的对象上使用注释,但我无法做到这一点(我无法在 DateTime 类上放置注释,因为我无法操作其源代码)。

我正在使用 Jersey-test 框架来测试这一点。

我的泽西端点:

@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);
}

我的 Jersey 测试:

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 错误。

最佳答案

首先要做的事情是:您需要一个用于 joda DateTime 的 MessageBodyReader/Writer

如果不进行一些调整,JSON 序列化日期时间的 POST 似乎将无法工作。 Gson 序列化的 DateTime 将如下所示:

{ "iMillis": 1414507195233,
  "iChronology": {
    "iBase": {
        "iBase": {
            ...
        },
        "iParam": {
            ...
        }}}}

如果您尝试使用 Gson 再次反序列化,Gson 将失败,因为 org.joda.time.Chronology-> iChronology 没有默认值 -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 中注册。

作者将回复 1414507195233(是的,不是 JSON),如果您将其发布回您的资源,您将获得一个有效的日期时间。

如果您获得像上面的示例一样的 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>

Jersey 2.12

希望这对您有所帮助。

关于json - 如何将 Joda DateTime 对象作为 HTTP POST 的消息正文传递到 Jersey REST 端点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26556743/

相关文章:

java - JSON获取没有对象的数组

javascript - 如何在JS中向具有特定参数/值的子对象添加属性?

java - 什么 xmlAdapter 使用 JAXB 来解码 base64Binary

java - Jersey JAXRS 过滤器,在访问资源(Web 服务)之前提供未编码的实体

java - 在其他 JSONObject 中处理 JSONObject

javascript - JSON遍历对象并访问数据结果无法读取未定义的属性

javascript - JSON.stringify,改变key的大小写

java - Java 8 中带有 JAXB 的 UTF-8 字符

java - 来自 xsd 的动态 java bean

java - jackson 映射到错误的对象