将自定义的 ObjectWriter 与 Jersey 一起使用

Using customized ObjectWriter with Jersey

我正在开发 REST 网络服务。 Jersey 作为 jax-rs 提供者,Jackson 为 serialization/deserialization。我也是基于Retrofit2开发客户端。

我的 class 层次结构由第三方库提供,所有 classes 都来自根基 class BaseObject。其中一些 classes 有不良吸气剂,例如isEmpty,我想在序列化时忽略(请注意,重要的是它们根本不被序列化,并且在我的情况下使用 FAIL_ON_UNKNOWN_PROPERTIES 反序列化是不够的)。

我使用 Mixins 在 BaseClass 上使用了 Jackson @JsonFilter。据我所知,要应用过滤器,必须使用以下内容:

new ObjectMapper().writer(filterProvider).writeValueAsString...

到目前为止一切正常:从生成的 json.

中成功过滤掉不需要的 属性

现在我必须配置 Jersey 和 Retrofit2 以使用我自定义的 json serializer/deserializer.

对于 Jersey,serialization/deserialization 可以使用实现 ContextResolver<ObjectMapper>Provider class 进行配置,并在 getContext(Class<?> type) 方法中返回自定义的 ObjectMapper .

与 Retrofit2 类似,通过使用 Retrofit.Builder().addConverterFactory(JacksonConverterFactory.create(objectMapper)),可以自定义serialization/deserialization.

问题是 new ObjectMapper().writer(filterProvider)ObjectWriter 类型而不是 ObjectMapper 类型。我怎样才能告诉 Jersey 和 Retrofit2 使用我自定义的 ObjectWriter,它使用我的过滤器?

从 Jackson 的 2.6 版开始,它具有用于 ObjectMapper 的 'setFilterProvider' 方法。 我没有尝试过,但文档对此有描述:https://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/ObjectMapper.html#setFilterProvider-com.fasterxml.jackson.databind.ser.FilterProvider-。我认为您可以尝试,因为描述适合您的情况。

我用 Jersey 2.7 和 Jackson 2.9.5 构建了一个测试服务。它工作正常,但你必须知道一些 运行 它的技巧。

在 pom.xml 添加泽西岛和杰克逊:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jersey.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>${jackson.version}</version>
    </dependency>
</dependencies>
<properties>
    <jersey.version>2.7</jersey.version>
    <jackson.version>2.9.5</jackson.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

你必须定义这个依赖:

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
    </dependency>

这是强制性的。

在 web.xml 中,您必须对服务配置进行引用:

<web-app version="2.5"
     xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>org.glassfish.jersey.server.ResourceConfig</param-name>
        <param-value>com.home.MyApplication</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

MyApplication.java:

package com.home;

import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;

import javax.ws.rs.ApplicationPath;

@ApplicationPath("/webapi")
public class MyApplication extends ResourceConfig {

    public MyApplication() {

        register(ObjectMapperProvider.class);
        register(JacksonFeature.class);
        register(MyResource.class);

    }
}

使用自定义 ObjectMapperProvider,您必须注册一个 JacksonFeature.class,因为没有它,Jersey 不会使用自定义 ObjectMapperProvider。

ObjectMapperProvider.java:

package com.home;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper>{

    final ObjectMapper defaultObjectMapper;

    public ObjectMapperProvider() {
        defaultObjectMapper = createDefaultMapper();
    }

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

    public static ObjectMapper createDefaultMapper() {

        final ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.setFilters(new SimpleFilterProvider().addFilter("dataFilter", SimpleBeanPropertyFilter.serializeAllExcept("region", "city")));
        return mapper;

    }
}

要定义过滤器,请使用 'setFilters' 方法。此方法已弃用,但调用 'jersey-hk2' 的 Jersey 库不知道新方法 'setFilterProvider' 并抛出异常。使用旧方法一切正常。

带有@JsonFilter 的业务对象:

@JsonFilter("dataFilter")
public class SimpleData {

    @JsonProperty("name")
    String firstName;

    @JsonProperty("secondName")
    String lastName;

    @JsonProperty("country")
    String country;

    @JsonProperty("region")
    String region;

    @JsonProperty("city")
    String city;

    @JsonProperty("genre")
    String genre;

    public SimpleData() {

        this.firstName = "Bryan";
        this.lastName = "Adams";

        this.country = "Canada";
        this.region = "Ontario";
        this.city = "Kingston";
        this.genre = "Rock";

    }

    public String getFirstName() {    return firstName;       }
    public void setFirstName(String firstName) {  this.firstName = firstName;  }
    public String getLastName() {   return lastName;    }
    public void setLastName(String lastName) {  this.lastName = lastName;       }
    public String getCountry() {    return country;    }
    public void setCountry(String country) {  this.country = country;      }
    public String getRegion() {   return region;     }
    public void setRegion(String region) {   this.region = region;       }
    public String getCity() {  return city;     }
    public void setCity(String city) {   this.city = city;     }
    public String getGenre() {   return genre;      }
    public void setGenre(String genre) {    this.genre = genre;     }
}

MyResource.java:

@Path("myresource")
public class MyResource {


    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public SimpleData getIt() {

        return new SimpleData();

    }
}

过滤后的结果: