带有 Jersey 2.2 和 Jackson 2.1 的自定义 ObjectMapper
Posted
技术标签:
【中文标题】带有 Jersey 2.2 和 Jackson 2.1 的自定义 ObjectMapper【英文标题】:Custom ObjectMapper with Jersey 2.2 and Jackson 2.1 【发布时间】:2013-09-23 06:05:56 【问题描述】:我正在努力使用 Grizzly、Jersey 和 Jackson 的 REST 应用程序,因为 Jersey 忽略了我的自定义 ObjectMapper。
POM 依赖:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>
产生的版本是:Grizzly 2.3.3、Jackson 2.1.4 和 Jersey 2.2。
主类(我想要显式注册 Jersey 组件):
public class Main
public static void main(String[] args)
try
ResourceConfig rc = new ResourceConfig();
rc.register(ExampleResource.class);
rc.register(ObjectMapperResolver.class);
HttpHandler handler = ContainerFactory.createContainer(
GrizzlyHttpContainer.class, rc);
URI uri = new URI("http://0.0.0.0:8080/");
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);
ServerConfiguration config = server.getServerConfiguration();
config.addHttpHandler(handler, "/");
server.start();
System.in.read();
catch (ProcessingException | URISyntaxException | IOException e)
throw new Error("Unable to create HTTP server.", e);
ObjectMapper 的上下文解析器:
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class ObjectMapperResolver implements ContextResolver<ObjectMapper>
private final ObjectMapper mapper;
public ObjectMapperResolver()
System.out.println("new ObjectMapperResolver()");
mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
@Override
public ObjectMapper getContext(Class<?> type)
System.out.println("ObjectMapperResolver.getContext(...)");
return mapper;
ObjectMapperResolver
构造函数和 getContext
都不会被调用。我错过了什么?我更喜欢使用 Jersey 2.2 和 Jackson 2.1,因为它是另一个库的依赖项。
完整的例子可以在 GitHub 上找到:https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/***
【问题讨论】:
你到底在纠结什么? 泽西忽略了我的ObjectMapperResolver
。我用rc.register(ObjectMapperResolver.class);
注册了它,但是这不起作用。
你的构造函数和getContext方法被调用了吗?为什么你有这个注解:@Produces(MediaType.APPLICATION_JSON)?我会删除它。
构造函数和 getContext 都没有被调用。 @Produces 没有理由...
我用这个说明解决了我的问题...REST Web Services with Jackson, Jersey and Payara Micro ... Level II
【参考方案1】:
以下解决方案适用于以下堆栈(如...这是我用来测试它的设置)
泽西 2.12,杰克逊 2.4.x
我将我的消息与我在这篇文章中提出的解决方案一起添加,因为它与我今天输入的许多 Google 搜索非常相关......对于我所相信的而言,这是一个繁琐的解决方案成为一个更麻烦的问题。
1。确保您的 maven 配置包含 jackson-jaxrs-json-provider
依赖项:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.1</version>
</dependency>
2。确保您的 Maven 配置不包含 jersey-media-json-jackson
依赖项:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
3。创建一个扩展com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider
的@Provider
组件,如下所示:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class CustomJsonProvider extends JacksonJaxbJsonProvider
private static ObjectMapper mapper = new ObjectMapper();
static
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enable(SerializationFeature.INDENT_OUTPUT);
public CustomJsonProvider()
super();
setMapper(mapper);
如您所见,这也是我们定义 com.fasterxml.jackson.databind.ObjectMapper
的自定义实例的地方
4。像这样通过MarshallingFeature
扩展javax.ws.rs.core.Feature
:
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
public class MarshallingFeature implements Feature
@Override
public boolean configure(FeatureContext context)
context.register(CustomJsonProvider.class, MessageBodyReader.class, MessageBodyWriter.class);
return true;
5。您需要像这样注册此自定义提供程序,前提是您通过 org.glassfish.jersey.server.ResourceConfig
像这样配置您的应用程序:
import org.glassfish.jersey.server.ResourceConfig;
...
public class MyApplication extends ResourceConfig
public MyApplication()
...
register(MarshallingFeature.class);
...
其他说明和意见:
-
无论您是否使用
javax.ws.rs.core.Response
包装控制器的响应,此解决方案都适用。
请确保您仔细考虑(复制/粘贴)以下代码 sn-ps,因为唯一的“非强制性”可以说是与 com.fasterxml.jackson.databind.ObjectMapper
的自定义配置有关的位。
@jcreason
很抱歉,@jcreason 把球丢在了这个上面,我希望你仍然是古玩。 所以我检查了去年的代码,这就是我想出的提供自定义映射器的方法。
问题在于,在功能初始化期间,任何自定义对象映射器都会被
中的某些代码禁用org.glassfish.jersey.jackson.JacksonFeature:77 (jersey-media-json-jackson-2.12.jar)
// Disable other JSON providers.
context.property(PropertiesHelper.getPropertyNameForRuntime(InternalProperties.JSON_FEATURE, config.getRuntimeType()), JSON_FEATURE);
但是这个特性只能被这个组件注册
org.glassfish.jersey.jackson.internal.JacksonAutoDiscoverable
if (!context.getConfiguration().isRegistered(JacksonFeature.class))
context.register(JacksonFeature.class);
所以我所做的是注册我自己的功能,该功能注册我自己的对象映射器提供程序并放入绊线停止 org.glassfish.jersey.jackson.JacksonFeature 被注册并覆盖我的对象映射器......
import com.fasterxml.jackson.jaxrs.base.JsonMappingExceptionMapper;
import com.fasterxml.jackson.jaxrs.base.JsonParseExceptionMapper;
import org.glassfish.jersey.internal.InternalProperties;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
public class MarshallingFeature implements Feature
private final static String JSON_FEATURE = MarshallingFeature.class.getSimpleName();
@Override
public boolean configure(FeatureContext context)
context.register(JsonParseExceptionMapper.class);
context.register(JsonMappingExceptionMapper.class);
context.register(JacksonJsonProviderAtRest.class, MessageBodyReader.class, MessageBodyWriter.class);
final Configuration config = context.getConfiguration();
// Disables discoverability of org.glassfish.jersey.jackson.JacksonFeature
context.property(
PropertiesHelper.getPropertyNameForRuntime(InternalProperties.JSON_FEATURE,
config.getRuntimeType()), JSON_FEATURE);
return true;
这是自定义对象映射器提供程序...
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JacksonJsonProviderAtRest extends JacksonJaxbJsonProvider
private static ObjectMapper objectMapperAtRest = new ObjectMapper();
static
objectMapperAtRest.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapperAtRest.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapperAtRest.configure(SerializationFeature.INDENT_OUTPUT, true); // Different from default so you can test it :)
objectMapperAtRest.setSerializationInclusion(JsonInclude.Include.ALWAYS);
public JacksonJsonProviderAtRest()
super();
setMapper(objectMapperAtRest);
【讨论】:
3.不编译 - 找不到符号“objectMapperAtRest”。这只是为了成为“映射器”吗? 我无法让它工作。获取 org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class我找到了解决方案。我必须自己实例化 Jackson Provider 并设置我的自定义 ObjectMapper
。可以在 GitHub 上找到一个工作示例:https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/***-answer
我删除了我的ObjectMapperResolver
并修改了我的main
方法:
public class Main
public static void main(String[] args)
try
// create custom ObjectMapper
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// create JsonProvider to provide custom ObjectMapper
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(mapper);
// configure REST service
ResourceConfig rc = new ResourceConfig();
rc.register(ExampleResource.class);
rc.register(provider);
// create Grizzly instance and add handler
HttpHandler handler = ContainerFactory.createContainer(
GrizzlyHttpContainer.class, rc);
URI uri = new URI("http://0.0.0.0:8080/");
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);
ServerConfiguration config = server.getServerConfiguration();
config.addHttpHandler(handler, "/");
// start
server.start();
System.in.read();
catch (ProcessingException | URISyntaxException | IOException e)
throw new Error("Unable to create HTTP server.", e);
【讨论】:
这非常很有帮助,但它仍然有一个问题:Jersey 自动发现 Jackson 提供程序,因此它创建了一个(可能未配置的)提供程序,然后此代码显式注册一个(正确配置的)提供者,所以 Jersey 最终有 两个 提供者。我已经在 (github.com/svenwltr/example-grizzly-jersey-jackson/issues/1) 上说明了这一点。我不知道如何解决这个问题。有什么想法吗? 我相信你可以关闭自动发现,但你会失去它。 给定的例子对我有用。另外,尝试 user915662s 解决方案。 当我尝试使用 protos 运行一些测试用例时,这对我有用。不错 您可以使用resourceConfig.addProperties(Collections.singletonMap(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true))
禁用自动发现【参考方案3】:
我想通了,基于一些修补。
问题似乎出在 Jersey 的功能自动检测机制中。如果您依赖 Jersey 加载 JacksonJaxbJsonProvider,则您的 ObjectMapper 的自定义上下文提供程序将被忽略。相反,如果您手动注册该功能,它就可以工作。我假设这与将自动检测到的提供程序加载到不同的上下文范围有关,但至于解决方案,这就是我最终得到的。请注意,我将它包装成一个功能,您应该可以直接在您的应用程序中注册它而不会出现问题。
public final class RequestMappingFeature implements Feature
@Override
public boolean configure(final FeatureContext context)
context.register(ObjectMapperProvider.class);
// If you comment out this line, it stops working.
context.register(JacksonJaxbJsonProvider.class);
return true;
2017 年 11 月更新:Jersey2 世界发生了一些变化。如果上述方法不起作用,请尝试以下操作:
提供您自己的 ObjectMapper 的新方法现在如下所示:
public final class JacksonFeature implements Feature
private static final ObjectMapper MAPPER;
static
// Create the new object mapper.
MAPPER = new ObjectMapper();
// Enable/disable various configuration flags.
MAPPER.configure(
DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
// ... Add your own configurations here.
@Override
public boolean configure(final FeatureContext context)
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider(
MAPPER, DEFAULT_ANNOTATIONS);
context.register(provider);
return true;
【讨论】:
添加此功能只会给我“警告:未找到资源类的资源方法... RequestMappingFeature” “新方法”适用于 Jersey 2.27 和 Jackson 2.9 适用于 Jersey 2.23.2 和 Jackson 2.5.4,并且比其他解决方案 AFAICT 简单得多。为了让下次更简单,DEFAULT_ANNOTATIONS
是 JacksonJaxbJsonProvider
上的一个字段,而你是 ResourceConfig.register(JacksonFeature.class)
。【参考方案4】:
请这样做:
1) 添加 pom.xml 依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.2</version>
</dependency>
2) 在 Main.java 中注册 JacksonFeature
public class Main
public static void main(String[] args)
try
ResourceConfig rc = new ResourceConfig();
rc.register(ExampleResource.class);
rc.register(ObjectMapperResolver.class);
rc.register(JacksonFeature.class);
HttpHandler handler = ContainerFactory.createContainer(
GrizzlyHttpContainer.class, rc);
URI uri = new URI("http://0.0.0.0:8080/");
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri);
ServerConfiguration config = server.getServerConfiguration();
config.addHttpHandler(handler, "/");
server.start();
System.in.read();
catch (ProcessingException | URISyntaxException | IOException e)
throw new Error("Unable to create HTTP server.", e);
3) 在您的资源中使用 org.codehaus.jackson.map.ObjectMapper
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig.Feature;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class ObjectMapperResolver implements ContextResolver<ObjectMapper>
private final ObjectMapper mapper;
public ObjectMapperResolver()
System.out.println("new ObjectMapperResolver()");
mapper = new ObjectMapper();
mapper.enable(Feature.INDENT_OUTPUT);
@Override
public ObjectMapper getContext(Class<?> type)
System.out.println("ObjectMapperResolver.getContext(...)");
return mapper;
【讨论】:
这行得通,但我必须降级到 Jackson 1.9。我更喜欢杰克逊 2.1。抱歉,我没有提到。 对于我们这些迟到的人来说 - 它仍然出现在最新的 Jackson 2.x (v2.3) 中。此答案使用依赖于 Jackson 1.x 的较旧的 Jersey/Jackson 连接器库。这里的全部问题是在 Jackson 1.x 中有效的在 2.x 中无效。您现在必须手动注册 Jackson 提供程序(关闭 METAINF 自动发现)。 我不明白为什么这个类上有@Produces
注释。这不是泽西资源..!【参考方案5】:
由于我花了几个小时才将它与 Java EE7 和 Glassfish4 一起使用,所以这是我的解决方案:
@javax.ws.rs.ApplicationPath("withJackson")
public class ApplicationConfig extends Application
private static final Logger log = java.util.logging.Logger.getLogger(ApplicationConfig.class.getName());
@Override
public Set<Object> getSingletons()
Set<Object> set = new HashSet<>();
log.log(Level.INFO, "Enabling custom Jackson JSON provider");
set.add(new JacksonJsonProvider().configure(SerializationFeature.INDENT_OUTPUT, true));
return set;
@Override
public Map<String, Object> getProperties()
Map<String, Object> map = new HashMap<>();
log.log(Level.INFO, "Disabling MOXy JSON provider");
map.put("jersey.config.disableMoxyJson.server", true);
return map;
@Override
public Set<Class<?>> getClasses()
Set<Class<?>> resources = new java.util.HashSet<>();
addRestResourceClasses(resources);
return resources;
/**
* Do not modify addRestResourceClasses() method.
* It is automatically populated with
* all resources defined in the project.
* If required, comment out calling this method in getClasses().
*/
private void addRestResourceClasses(Set<Class<?>> resources)
resources.add(com.fasterxml.jackson.jaxrs.base.JsonMappingExceptionMapper.class);
resources.add(com.fasterxml.jackson.jaxrs.base.JsonParseExceptionMapper.class);
resources.add(com.fasterxml.jackson.jaxrs.json.JsonMappingExceptionMapper.class);
resources.add(com.fasterxml.jackson.jaxrs.json.JsonParseExceptionMapper.class);
resources.add(de.lathspell.java_test_ee7_json.Api.class);
resources.add(de.lathspell.java_test_ee7_json.with_jackson.MyExceptionMapper.class);
唯一相关的 POM 依赖项是:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
【讨论】:
+1 formap.put("jersey.config.disableMoxyJson.server", true);
,这让我挣扎了好几个小时。谢谢!
谢谢,这对我有用(SDK8,Jersey 2.10.x)只是一个备注,如果不是第一次添加,则不需要禁用 Moxy。【参考方案6】:
来自 Jersey 2.17 文档: https://eclipse-ee4j.github.io/jersey.github.io/documentation/2.17/media.html#jackson-registration
在应用程序中
@ApplicationPath("/")
public class MyApplication extends ResourceConfig
public MyApplication()
register(JacksonFeature.class);
// This is the class that you supply, Call it what you want
register(JacksonObjectMapperProvider.class);
//...
编辑,忘记添加您在 register(..) 中提供的 JacksonObjectMapperProvider:
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
@Provider
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper>
final ObjectMapper defaultObjectMapper;
public JacksonObjectMapperProvider()
defaultObjectMapper = createDefaultMapper();
@Override
public ObjectMapper getContext(Class<?> type) return defaultObjectMapper;
public static ObjectMapper createDefaultMapper()
final ObjectMapper jackson = new ObjectMapper();
// any changes to the ObjectMapper is up to you. Do what you like.
// The ParameterNamesModule is optional,
// it enables you to have immutable POJOs in java8
jackson.registerModule(new ParameterNamesModule());
jackson.enable(SerializationFeature.INDENT_OUTPUT);
jackson.disable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
jackson.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return jackson;
【讨论】:
这不是问题的答案。我想你错过了他试图注册一个 custom 映射器的点,而不是默认的。 它有效。new ParameterNamesModule()
对象中有什么?
createDefaultMapper()
方法提供了自定义对象映射器。创建任何 ObjectMapper 并按照您希望它在该方法中的工作方式对其进行配置。 ParameterNamesModule (github.com/FasterXML/jackson-modules-java8/tree/master/…) 只是您可以配置的一个示例。【参考方案7】:
使用 Jackson 2.7,这不起作用:
public class MyApplication extends ResourceConfig
public MyApplication()
register(MyObjectMapperProvider.class);
调用了 MyObjectMapperProvider 构造函数,但从未调用过 getContext()。
在 super() 构造函数中注册 MyObjectMapperProvider 使其工作:
public class MyApplication extends ResourceConfig
public MyApplication()
super(
// register Jackson ObjectMapper resolver
MyObjectMapperProvider.class
);
见this Jersey example code。
【讨论】:
以上是关于带有 Jersey 2.2 和 Jackson 2.1 的自定义 ObjectMapper的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring MVC 中使用 Jackson 和 Jersey 2 Client 反序列化 Joda DateTime?
带有 Jersey 客户端版本 2.2 的 Restful WebService 调用
使用 JAX-RS Jersey 2.2 获取带有 Content-Type 和 Accept 标头的请求
Jersey Jackson和codehaus vs. fasterxml