【问题标题】:Jackson 2 incorrectly serializing Java java.nio.file.PathJackson 2 错误地序列化 Java java.nio.file.Path
【发布时间】:2016-11-11 23:30:25
【问题描述】:

我在 Eclipse 4.6.1 中使用 RESTEasy 3.1.0.CR3 和 Tomcat 8.5.6,以及 JSBoss resteasy-jackson2-provider。我有一个简单的 JavaBean FooBar,它返回一个字符串“ID”和一个 java.nio.file.Path“路径”。

Jackson 使序列化为 JSON 变得容易。在我的 JAX-RS 资源中,我只需指定 @Produces("application/json; charset=UTF-8")。但是杰克逊没有使用Path.toString()。相反,它似乎在使用Path.toURI().toString() 或其他东西:

{
  "id": "foo",
  "path": "file:///C:/Users/jdoe/bar"
}

为什么!??更重要的是,我怎样才能让杰克逊简单地使用PathtoString() 版本?

这是我的项目依赖树:

+- com.google.code.findbugs:jsr305:jar:3.0.1:provided
+- com.google.guava:guava:jar:20.0:compile
+- javax.ws.rs:javax.ws.rs-api:jar:2.0.1:provided
+- org.jboss.resteasy:resteasy-jaxrs:jar:3.1.0.CR3:compile
|  +- org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.0_spec:jar:1.0.1.Beta1:compile
|  +- org.jboss.resteasy:resteasy-jaxrs-services:jar:3.1.0.CR3:compile
|  +- org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec:jar:1.0.0.Final:compile
|  +- javax.activation:activation:jar:1.1.1:compile
|  +- org.apache.httpcomponents:httpclient:jar:4.5.2:compile
|  |  +- org.apache.httpcomponents:httpcore:jar:4.4.4:compile
|  |  +- commons-logging:commons-logging:jar:1.2:compile
|  |  \- commons-codec:commons-codec:jar:1.9:compile
|  +- commons-io:commons-io:jar:2.5:compile
|  +- net.jcip:jcip-annotations:jar:1.0:compile
|  \- org.jboss.logging:jboss-logging:jar:3.3.0.Final:compile
+- org.jboss.resteasy:resteasy-servlet-initializer:jar:3.1.0.CR3:compile
+- org.jboss.resteasy:resteasy-jackson2-provider:jar:3.1.0.CR3:compile
|  +- com.fasterxml.jackson.core:jackson-core:jar:2.8.3:compile
|  +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.3:compile
|  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.3:compile
|  \- com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:2.8.3:compile
|     +- com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:2.8.3:compile
|     \- com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:2.8.3:compile
+- junit:junit:jar:4.12:test
|  \- org.hamcrest:hamcrest-core:jar:1.3:test
\- org.hamcrest:hamcrest-library:jar:1.3:test

请注意,我可以向我的 FooBar 类添加注释,这是一个不应与 RESTful API 序列化细节耦合的域模型类。

我想要一种简单的方法来挂钩到 RESTEasy 中的 Jackson2 并修改 Path 属性值的序列化,而无需修改我的类或为我的类编写自定义序列化程序。

【问题讨论】:

  • 你能分享 Jackson 版本吗,因为至少 2.7.0 使用 ToStringSerializer 来序列化 Path。
  • 嗯,但是你能提供你的项目解决的jackson-core版本吗
  • 我为你添加了完整的依赖树。
  • 您在此版本中使用的是 2.8.3,他们已添加 NioPathSerializer 并调用 path.toUri().toString()。尝试将jackson的版本降级到2.7.X

标签: json serialization jackson resteasy


【解决方案1】:

这个类会覆盖默认的 Jackson2JsonProvider 帮助你配置 ObjectMapper,根据你的需要,比如 Path.class 的序列化器被 NioPathSerializer 覆盖到 ToStringSerializer。

@Provider
@Consumes({"application/*+json", "text/json"})
@Produces({"application/*+json", "text/json"})
public class CustomJacksonProvider extends JacksonJsonProvider {

  public CustomJacksonProvider(){
    super(configureMapper(new ObjectMapper()));
  }

  public static ObjectMapper configureMapper(ObjectMapper mapper) {
    SimpleModule m = new SimpleModule("PathToString");
    m.addSerializer(Path.class,new ToStringSerializer());
    mapper.registerModule(m);
    return mapper;
  }

}

或者您也可以使用 ContextResolver 来使用 ResteasyJackson2Provider(RestEasy default)

@Provider
@Consumes({"application/*+json", "text/json"})
@Produces({"application/*+json", "text/json"})
public class CustomJacksonProvider implements ContextResolver<ObjectMapper> {

  final ObjectMapper mapper;

  public CustomJacksonProvider(){
    mapper = new JsonMapperConfigurator(new ObjectMapper(), JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS)
            .getConfiguredMapper();
    SimpleModule m = new SimpleModule("PathToString");
    m.addSerializer(Path.class, new ToStringSerializer());
  }

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

无论您使用什么方法,都可以添加 web.xml 给您:

    <context-param>
       <param-name>resteasy.providers</param-name>
       <param-value>package.to.CustomJacksonProvider</param-value>
    </context-param>

如果您没有使用 web.xml 文件,请在您的 JAX-RS 应用程序的 Application.getClasses() 方法中返回提供程序类。

希望这会有所帮助。

已编辑

此外,您可能会错过此实现中的 JAXB 注释支持,您必须手动配置它,因为 JacksonJaxbJsonProvider 不会寻找 JsonMapperConfigurator 的解析器/提供者。使用JsonMapperConfigurationCustomJackosnProvider 中的映射器以及JacksonJaxbJsonProvider.DEAFULT_ANNOATIONS。理想情况下,由于没有规定提供JsonMapperConfiguration Resolver/Provider 并且 RestEasy 的 jackson 提供程序不公开 arg 构造函数,因此最好选择提供程序。由于他们做出了这个决定,理想情况下他们不应该在ResteasyJackson2Provider 中进行任何自定义。此外,ResteasyJackson2Provider 中的唯一功能是自定义 json 类型和更好的类级别映射器缓存。

根据说明更新代码

【讨论】:

  • 如果我想尽可能做到无创,我会使用哪种方法?似乎在第一种方法中(除非我扩展 ResteasyJackson2Provider——但我想将 RESTEasy 特定的东西排除在我的代码之外)我会丢弃 ResteasyJackson2Provider 可能提供的任何功能。无论哪种方式,我似乎都在创建一个新的ObjectMapper。但是ResteasyJackson2Provider 的代码似乎在查找现有的ObjectMapper。我是否会通过创建新功能来丢弃某些内置功能?
  • 哇哦——它有效!非常感谢您清晰简洁地列出解决此问题的步骤。 (我希望得到澄清;请参阅其他评论。)我编辑了答案以包含有关在非web.xml 配置中注册课程的说明。抱歉,我花了这么长时间来审查这个,赏金自动奖励为 50%。你给出了一个非常有用的答案,以至于我想开始另一个赏金只是为了确保你得到你应得的,但它迫使第二个赏金非常高。 :(
  • 我会推荐第二个,因为 ContextResolver 就是为此目的而设计的,不会遗漏 reasteasy 提供的任何其他功能
  • 但是在创建我自己的ObjectMapper 时,RESTEasy 添加到它的ObjectMapper 中是否会遗漏一些东西?还是将所有ObjectMappers 合并?它是如何工作的? (或者也许指向我的文档。)
  • @Consumes@Produces 真的适合 ContextResolver 实现吗?它们不只适用于消息正文的读者和作者吗?它们在第二个例子中有什么用途吗? (我现在在 JAX-RS 规范中看到它们是允许的——但在这种情况下它们是必需的还是有用的?它们是如何使用的?)
猜你喜欢
  • 1970-01-01
  • 2016-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-23
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多