Jersey解析Java 8日期时间

11

这是我的用户类,我希望在我的数据库中保存符合ISO标准的日期时间。

public class User  {

    @Id
    private String id;

    private String email;

    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    private LocalDateTime loginDate;

 }

这是我的Jersey控制器:

@POST
@Consumes("application/json")
@Produces("application/json")

public Response create(  User user) {

    Map<Object, Object> apiResponse = new HashMap<Object, Object>();
    Map<Object, Object> response  = new HashMap<Object, Object>();


    user = (User) userService.create(user);

}

我如何在Jersey中使用这种日期时间格式? 是否可以发送日期时间 String 并自动创建Java 8日期时间对象?

{        
    "email" : "imz.mrz@gmail.com"
    "loginDate" : "2015-04-17T06:06:51.465Z"
} 

更新:

我使用的是Spring Boot Jersey,并且安装了其他JSR包。

  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-jersey</artifactId>
 </dependency>

因此,我删除了除spring-boot-jersey包之外的所有软件包。 使用此注释来处理LocalDateTime

  @JsonDeserialize(using =  LocalDateTimeDeserializer.class)
这样,我可以消耗ISO日期并将ISODate()保存到mongodb中,并生成完整格式的mongodb本地日期时间到前端。
问题解决了。

很可能有人已经编写了一个库来完成这个任务,如果没有,你可能需要编写自己的定制转换器。请参阅http://blog.dejavu.sk/2014/02/11/inject-custom-java-types-via-jax-rs-parameter-annotations/。 - BretC
1个回答

17

我看到的几种选择...

选项1:

假设您使用Jackson作为JSON提供程序,并具有JAXB注释支持...

您可以使用一个 XmlAdapter。例如,

public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {

    @Override
    public LocalDateTime unmarshal(String dateString) throws Exception {
        Instant instant = Instant.parse(dateString);
        LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        return dateTime;
    }

    @Override
    public String marshal(LocalDateTime dateTime) throws Exception {
        Instant instant = dateTime.toInstant(ZoneOffset.UTC);
        return DateTimeFormatter.ISO_INSTANT.format(instant);
    }
}

有关更多信息,请参阅Instant API

然后,您只需使用适配器对该字段/属性进行注释即可。

@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
private LocalDateTime loginDate;

您还可以在包级别上声明注解,这样包中的所有使用都将使用适配器,无需进行注释。 在名为package-info.java的文件中放置该注解即可。

@XmlJavaTypeAdapters({
    @XmlJavaTypeAdapter(type = LocalDateTime.class, 
                        value = LocalDateTimeAdapter.class)
})
package thepackage.of.the.models;

import java.time.LocalDateTime;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;

选项 2:

直接使用 Jackson API。也就是说,使用 JsonDeserializerJsonSerializer。例如:

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {

    @Override
    public LocalDateTime deserialize(JsonParser jp, 
            DeserializationContext dc) throws IOException, JsonProcessingException {
        ObjectCodec codec = jp.getCodec();
        TextNode node = (TextNode)codec.readTree(jp);
        String dateString = node.textValue();
        Instant instant = Instant.parse(dateString);
        LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        return dateTime;
    } 
}

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    @Override
    public void serialize(LocalDateTime dateTime, JsonGenerator jg, 
            SerializerProvider sp) throws IOException, JsonProcessingException {
        Instant instant = dateTime.toInstant(ZoneOffset.UTC);
        jg.writeString(DateTimeFormatter.ISO_INSTANT.format(instant));
    } 
}
您可以在字段/属性级别应用此项。
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
public LocalDateTime loginDate; 

或者在ObjectMapper级别进行操作(这样你就不需要在每个地方添加注释)

@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

    final ObjectMapper mapper = new ObjectMapper();

    public ObjectMapperContextResolver() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        mapper.registerModule(module);
        // add JAXB annotation support if required
        mapper.registerModule(new JaxbAnnotationModule());
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return mapper;
    }  
}
基本上发生的事情是,用于反序列化/序列化的MessageBodyWriter/MessageBodyReader将调用getContext方法以获取ObjectMapper。
注意:
- 上述解决方案将从格式2007-12-03T10:15:30.00Z进行解析,如Instant.parse中所述,并将序列化为相同的格式,如DateTimeFormatter.ISO_INSTANT中所述。 - 上述假定您正在使用Jackson作为序列化程序。我使用了以下依赖项(与Jersey 2.16一起)进行测试。
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.16</version>
</dependency>

该依赖项使用 JacksonJaxbJsonProvider 以支持 JAXB 注解。如果你使用的是较低版本的 Jersey,例如 1.x,则 jersey-json 依赖应该提供了 JAXB 注解支持,如果你启用了 POJO 映射功能。或者对于 Jersey 1.x,如果你想要使用 Jackson 2,可以使用这个依赖项。

<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.4.0</version>
</dependency>

实际上是 jersey-media-json-jackson 使用的内容,因此您可以明确注册 JacksonJaxbJsonProvider,或将 Jackson 包 (com.fasterxml.jackson.jaxrs.json) 添加到要扫描的包列表中。


更新

另请参见:


1
它对我来说运行得很好。你使用的是哪种配置?是注释还是ObjectMapper? - Paul Samsotha
2
ContextResolver 中,我忘记注册 SimpleModule :-) mapper.registerModule(module); 已进行编辑。 - Paul Samsotha
我正在使用注解,如果我使用ObjectMapper,我应该如何使用它? - Imtiaz Mirza
1
注释应该可以工作。我已经测试了上面的所有选项。至于如何使用ObjectMapper,如果您正在扫描所有资源类(用@Path注释的类),那么ContextResolver应该会自动与@Provider注释一起被拾取。不需要额外的配置。我会在getContext方法中放置一个打印语句,以查看在序列化时是否调用它。让我知道。您也尝试过反序列化吗?那个能工作吗? - Paul Samsotha
清理除了以下依赖的所有Jersey相关包: <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.5.2</version> </dependency>这解决了我的问题。最终我没有使用序列化。 感谢您的帮助。 - Imtiaz Mirza
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接