如何将Java Calendar对象的JSON表示传递给REST服务?

时间:2015-06-29 14:15:42

标签: java json calendar jackson jax-rs

以下是我的问题的简化版本。请考虑以下REST服务......

@Stateless
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/test")
public class TestResource {


    @POST
    public Response test(Calendar c) {
        System.out.println(c);
        return Response.ok(Response.Status.OK).build();
    }

    @GET
    @Path("/getCalendar")
    public Response getCalendar() {
        return Response.ok(toJSONString(Calendar.getInstance())).build();
    }

    public String toJSONString(Object object) {
        GsonBuilder builder = new GsonBuilder();
        builder.setDateFormat("yyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        Gson gson = builder.create();
        return gson.toJson(object);
    }
}

当然,这......

@ApplicationPath("/rest")
public class RestActivator extends Application {

}

我正在使用Postman,首先我通过向“http://localhost:8080/ProjectName/rest/test/getCalendar”发送GET请求来获取Calendar对象的JSON表示。然后我复制返回的JSON,这是

{
  "year": 2015,
  "month": 5,
  "dayOfMonth": 29,
  "hourOfDay": 10,
  "minute": 7,
  "second": 24
}

然后使用Postman我发送一个POST到'http://localhost:8080/ProjectName/rest/',上面有我回复的数据。然后抛出'javax.ws.rs.NotSupportedException:无法使用内容类型'。

如何修复此问题,以便服务可以处理Calendar对象(以及将Calendar对象作为字段的类)?

编辑:

是的我正在设置正确的内容类型,即application / json。

这是我得到的回应......

com.fasterxml.jackson.databind.JsonMappingException:无法从START_OBJECT令牌中反序列化java.util.Calendar的实例  在[来源:io.undertow.servlet.spec.ServletInputStreamImpl@4cf87599; line:1,column:1]

更新: 为了实现这一点,我使用了@peeskillets解决方案。

4 个答案:

答案 0 :(得分:3)

杰克逊一般只适用于JavaBean式POJO,而Calendar则不然。对于这些情况,Jackson允许我们创建自定义反序列化器。创建反序列化程序后,我们可以在ObjectMapper中使用ContextResolver进行注册。例如

public class CalendarDeserializer extends JsonDeserializer<Calendar>  {

    @Override
    public Calendar deserialize(JsonParser jp, DeserializationContext dc) 
            throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        int year = getInt("year", node);
        int month = getInt("month", node);
        int dayOfMonth = getInt("dayOfMonth", node);
        int hourOfDay = getInt("hourOfDay", node);
        int minute = getInt("minute", node);
        int second = getInt("second", node);

        Calendar c = Calendar.getInstance();
        c.set(year, month, dayOfMonth, hourOfDay, minute, second);
        return c;
    }

    private int getInt(String name, JsonNode node) {
        return (Integer) ((IntNode) node.get(name)).numberValue();
    } 
}

使用我们可以执行的ObjectMapper注册

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Calendar.class, new CalendarDeserializer());
mapper.registerModule(module);

要在我们的JAX-RS应用程序中使用ObjectMapper,我们可以在ContextResolver中创建它,如here所示。如果您还没有jackson-databind作为依赖项,则需要添加它。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson2.version}</version>
</dependency>

请注意,JAX-RS的Jackson JSON提供程序已经提供了上述依赖关系,因此如果您已经拥有Jackson JSON支持依赖项,则不需要它。

我已经使用您提供的JSON对其进行了测试,并且工作正常。

详细了解自定义反序列化程序here

答案 1 :(得分:1)

您需要在请求中传递正确的内容类型,并检查您是否提供正确的路径..

答案 2 :(得分:0)

所以经过一些研究并感谢那些回答的人(特别是@peeskillet),我已经找到了解决这个问题的2个方法。

解决方案1 ​​

使用@peeskillet的答案并创建自定义反序列化器。这是有效的,这是我为了让它工作而添加的两个课程......

import java.util.Calendar;

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.databind.module.SimpleModule;

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

    private final ObjectMapper mapper;

    public ObjectMapperContextResolver() {

        mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Calendar.class, new CalendarDeserializer());
        mapper.registerModule(module);
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
   }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return mapper;
    }
}

以及自定义反序列化器

import java.io.IOException;
import java.util.Calendar;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.IntNode;

public class CalendarDeserializer extends JsonDeserializer<Calendar>  {

    private int getInt(String name, JsonNode node) {
        return (Integer) ((IntNode) node.get(name)).numberValue();
    }

    @Override
    public Calendar deserialize(JsonParser jp, com.fasterxml.jackson.databind.DeserializationContext dc) throws IOException,
            JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        int year = getInt("year", node);
        int month = getInt("month", node);
        int dayOfMonth = getInt("dayOfMonth", node);
        int hourOfDay = getInt("hourOfDay", node);
        int minute = getInt("minute", node);
        int second = getInt("second", node);

        Calendar c = Calendar.getInstance();
        c.set(year, month, dayOfMonth, hourOfDay, minute, second);
        return c;
    } 
}

解决方案2

然而,我也想出了另一种可能更容易的解决方案。如果我在@GET方法中省略了GSON toJSONString(不确定我为什么要混合Jackson和GSON ......)并简单地返回Response.ok(Calendar.getInstance()).build();,杰克逊将Calendar对象转换为它的原始长值。当我使用Calendar对象作为方法参数将长值传递给@POST方法时,Jackson能够将其正确地反序列化为Calendar对象。

<强>结论

如果要将Calendar对象的JSON表示传递给REST端点,请将其作为长表示传递。

另一方面,如果您有JavaScript Date对象或自定义Date对象,则始终可以创建自定义反序列化器。

答案 3 :(得分:0)

我知道这是个老问题,但是我只是添加了一个长的而不是Calendar的构造函数,这样它可以反序列化

@POST
public Response test(long c) {
    System.out.println(c);
    return Response.ok(Response.Status.OK).build();
}

例如:

public class demo{

    String id;
    Calendar date;
    String tag;

    public demo(Calendar date, String tag) {
        this.date = date;
        this.tag = tag;
    }

    public demo(long date, String tag) {
        this.date = longToCalendar(date);
        this.tag = tag;
    }

    public Calendar longToCalendar(Long epoch) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(epoch);
        return calendar;
    }
}


@POST
public Response test(Demo demo) {
    System.out.println(demo);
    return Response.ok(Response.Status.OK).build();
}