【问题标题】:Accessing Jackson Object Mapper in RestEasy在 RestEasy 中访问 Jackson 对象映射器
【发布时间】:2012-01-19 20:51:54
【问题描述】:

我被要求美化来自 RestEasy 端点的默认 Jackson JSON。我对 Jackson 进行了一些研究,并编写了一些独立的代码来抑制空值、自定义数据格式等。现在的挑战是在 RestEasy 的 JSON 序列化中注入这些代码。

从论坛帖子来看,这在 Spring 中是微不足道的,但在 RestEasy 中似乎并非如此。我编写了一个 ContextResolver 并在 web.xml(在 Tomcat 上)的上下文参数中配置为 resteasy.provider,但这会阻止 webapp 在 Tomcat 上加载。

现在我正在尝试扩展 javax.ws.rs.core.Application 并提供 ContextResolver 但没有任何进展。这是直截了当的,有人做过吗?非常感谢任何帮助。

【问题讨论】:

  • 好的,我可以通过基于wiki.fasterxml.com/JacksonFAQJaxRs条目编写自定义JacksonJsonProvider来做到这一点。代码如下-

标签: json jackson resteasy


【解决方案1】:

我有很多工作要在我的应用程序中注册它,即使在我使用 spring boot 并且没有 web.xml 时使用 context-param 的提示也是如此,这就是我所做的

自定义提供者

package com.mageddo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;

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

    private ObjectMapper objectMapper = new ObjectMapper(); // my own object mapper

    @Override
    public ObjectMapper locateMapper(Class<?> type, MediaType mediaType) {
        return objectMapper;
    }
}

申请注册

META-INF/services/javax.ws.rs.ext.Providers

com.mageddo.CustomJacksonJsonProvider
org.jboss.resteasy.plugins.providers.jackson.UnrecognizedPropertyExceptionHandler

【讨论】:

    【解决方案2】:

    如果您使用的是 Jackson 2.2.x 提供程序,Resteasy 提供了一个 与 JAXB 提供程序中的注释类似的漂亮打印注释:

    org.jboss.resteasy.annotations.providers.jackson.Formatted
    

    这是一个例子:

    @GET
    @Produces("application/json")
    @Path("/formatted/{id}")
    @Formatted
    public Product getFormattedProduct()
    {
        return new Product(333, "robot");
    }
    

    如上例所示,@Formatted 注解将启用 底层 Jackson 选项“SerializationFeature.INDENT_OUTPUT”。

    ©RESTEasy User Guide.

    这不是一个全局解决方案,但您也可以将注释放在类上。

    【讨论】:

      【解决方案3】:

      如果您使用的是 Jackson2 提供程序,您需要做一些与上一个答案稍有不同的事情。默认情况下,此示例将漂亮地打印输出

      @Provider
      public class JSONProvider extends ResteasyJackson2Provider {
      
        @Override
        public void writeTo(Object value, Class<?> type, Type genericType, Annotation[]  annotations, MediaType json, MultivaluedMap<String, Object> headers, OutputStream body) throws IOException {
      
          ObjectMapper mapper = locateMapper(type, json);
          mapper.enable(SerializationFeature.INDENT_OUTPUT);
      
          super.writeTo(value, type, genericType, annotations, json, headers, body);
        }
      
      }
      

      并在您的 web-xml 中注册它,如果您没有启用自动注册,请将其添加到您的 resteasy.providers 上下文参数中

      【讨论】:

      • 但是这样映射器配置将在每次序列化时更改。如何配置一次mapper?例如,如果我需要注册一个杰克逊模块。我不想在每个 http 请求上重新注册它。
      【解决方案4】:

      我找到了一种修改 Jackson SerializationConfig 的更好方法 - 您可以使用 JAX-RS ContextResolver 拦截 ObjectMapper 的创建。

      @Provider
      @Produces(Array(MediaType.APPLICATION_JSON))
      class JacksonConfig extends ContextResolver[ObjectMapper] {
      
        val mapper = new ObjectMapper()
        mapper.getSerializationConfig.setSerializationInclusion(Inclusion.NON_NULL)
      
        def getContext(objectType: Class[_]) = mapper
      }
      

      您需要通过以下方式之一注册 RESTEasy:

      • 将其作为类或实例从 javax.ws.rs.core.Application 实现中返回
      • 使用 resteasy.providers 将其列为提供者
      • 让 RESTEasy 在您的 WAR 文件中自动扫描它。请参阅配置指南
      • 通过 ResteasyProviderFactory.getInstance().registerProvider(Class) 或 registerProviderInstance(Object) 手动添加

      参考:RESTEasy docs

      参考:Nicklas Karlsson on the JBoss forums

      请注意,这适用于 RESTEasy 2.3.2,它作为 JBoss 7.1.1.Final 中的一个模块提供,但似乎不适用于 RESTEasy 3.0-beta5。

      【讨论】:

      • 你是救生员!非常感谢!
      【解决方案5】:

      好的,我想通了,我可以通过基于Jackson FAQ: JAX-RS编写自定义JacksonJsonProvider来做到这一点。代码如下:

      @Provider
      public class QBOJacksonJsonProvider extends JacksonJsonProvider {
          public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
      
          @Override
          public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
              Log.info(getClass(), "In custom JSON provider");
              //get the Object Mapper
              ObjectMapper mapper = locateMapper(type, mediaType);
              // Suppress null properties in JSON output
              mapper.getSerializationConfig().setSerializationInclusion(org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_NULL);
              // Set human readable date format
              SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
              mapper.getSerializationConfig().setDateFormat(sdf);
      
              super.writeTo(value, type, genericType, annotations, mediaType, httpHeaders, entityStream);
          }
      }
      

      【讨论】:

      • 你是如何“注册”这个 Provider 的?
      • 由于 Jackson 1.8 setDateFormat() 已被弃用,请使用 mapper.getSerializationConfig().withDateFormat()
      • @DanielSerodio 可以在 web.xml 中完成 &lt;context-param&gt; &lt;param-name&gt;resteasy.providers&lt;/param-name&gt; &lt;param-value&gt;com.package.JsonProvider&lt;/param-value&gt; &lt;/context-param&gt;
      • 这是覆盖从外部库中提取的自定义 ObjectMapper 并确保使用我自己的 ObjectMapper 的唯一方法(通过覆盖 locateMapper 方法返回我的 ObjectMapper)。
      【解决方案6】:

      Jackson ObjectMapper 的提供者应该是标准的 JAX-RS 方式(适用于 Jersey),所以这似乎也是 RESTeasy 的方式。

      【讨论】:

      • 感谢您的回复,您能详细说明一下吗?如果您能告诉我您是如何注册自定义对象映射器/序列化器/上下文解析器等的,那就太好了。
      • 好的,谢谢,我想通了。我正在与不相关的问题作斗争,这很简单,我编写了一个自定义 JacksonJsonProvider 如下 -
      猜你喜欢
      • 2014-09-18
      • 1970-01-01
      • 1970-01-01
      • 2018-03-17
      • 2012-07-08
      • 1970-01-01
      • 2012-09-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多