为 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;
我将 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;