为 JAX-RS 格式化 LocalDateTime

Formatting LocalDateTime for JAX-RS

我将 resteasy-jaxrs 与 jackson-datatype-jsr310 结合使用以在响应中序列化 LocalDateTime。这适用于我的 classes 中的属性,因为我可以添加必要的注释,如

@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
public LocalDateTime getExpirationDate() {
    return expirationDate;
}

使用 LocalDateTimeAdapter

public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {
    @Override
    public LocalDateTime unmarshal(String s) throws Exception {
        return LocalDateTime.parse(s);
    }

    @Override
    public String marshal(LocalDateTime dateTime) throws Exception {
        return dateTime.toString();
    }
}

在 json 我得到类似

的东西
"expirationDate": "2026-07-17T23:59:59"

但是我有一个对象地图,这个地图也可以有 LocalDateTime 类型的项目,对于那些没有注释的项目,所以我在 json 响应中得到了完整的对象,比如

"effectiveDate":       {
  "dayOfMonth": 1,
  "dayOfWeek": "FRIDAY",
  "month": "JUNE",
  "year": 2018, ...

有没有办法格式化每个 LocalDateTime 字段,无论它来自哪里?

保罗回答后更新

因为我已经添加了 com.fasterxml.jackson.datatype:jackson-datatype-jsr310 依赖项,所以我添加了 ObjectMapperContextResolver class 并从我的 LocalDateTime 属性 中删除了注释。遗憾的是,所有 LocalDateTimes 现在又被序列化为完整对象。在 post 的评论中,我看到有人向 web.xml 添加了一个上下文参数,以便 ObjectMapperContextResolver 被拾取。将其添加到我的 web.xml 后,它看起来像这样。

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">
    <display-name>Servlet 3.1 Web Application</display-name>
    <listener>
        <listener-class>com.kopi.web.InitListener</listener-class>
    </listener>
    <context-param>
        <param-name>resteasy.resources</param-name>
        <param-value>com.kopi.utils.ObjectMapperContextResolver</param-value>
    </context-param>
</web-app>

现在,由于添加了上下文参数 resteasy.resources,Web 应用程序不再启动,原因是

SEVERE: Servlet [com.kopi.rest.RestApplication] in web application [/kopi] threw load() exception
java.lang.RuntimeException: RESTEASY003130: Class is not a root resource.  It, or one of its interfaces must be annotated with @Path: com.kopi.utils.ObjectMapperContextResolver implements:  javax.ws.rs.ext.ContextResolver
    at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:179)
    at org.jboss.resteasy.core.ResourceMethodRegistry.addResourceFactory(ResourceMethodRegistry.java:158)
    at org.jboss.resteasy.core.ResourceMethodRegistry.addPerRequestResource(ResourceMethodRegistry.java:77)
    at org.jboss.resteasy.spi.ResteasyDeployment.registration(ResteasyDeployment.java:482)
    at org.jboss.resteasy.spi.ResteasyDeployment.startInternal(ResteasyDeployment.java:279)
    at org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:86)
    at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:119)
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:36)
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1194)
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1110)
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1000)
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4902)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5212)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:724)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:700)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:596)
    at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1805)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

在其他评论中我看到版本可能是个问题。所以我目前正在使用

org.jboss.resteasy:resteasy-jaxrs 3.5.1.Final
org.jboss.resteasy:resteasy-jackson-provider 3.5.1.Final
org.jboss.resteasy:resteasy-servlet-initializer 3.5.1.Final
com.fasterxml.jackson.datatype:jackson-datatype-jsr310 2.9.5

谢谢你,kopi

您无需在各个属性上声明 XmlAdapter,您只需使用 ContextResolver 通过 Jackson 对其进行全局配置。在这里您可以配置 ObjectMapper 并注册 JavaTimeModule with the mapper. This configuration will be global so you don't need to use the XmlAdapter. To see how you can configure this, you can see .

@XmlJavaTypeAdapter 可以应用于整个包 (see doc)。

When @XmlJavaTypeAdapter annotation is defined at the package level it applies to all references from within the package to @XmlJavaTypeAdapter.type().

尝试将文件 package-info.java 添加到具有地图的 class 旁边。鉴于 class 的包是 com.acme.beans,适配器的包是 com.acme.adapters,文件的内容应该是这样的:

@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
package com.acme.beans;

import com.acme.adapters.LocalDateTimeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;