以下是我的问题的简化版本。请考虑以下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解决方案。
答案 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();
}