将自定义的 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();
}
}
过滤后的结果:
我正在开发 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();
}
}
过滤后的结果: