Java 带有 Jersey 2.2 和 Jackson 2.1 的自定义 ObjectMapper
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18872931/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom ObjectMapper with Jersey 2.2 and Hymanson 2.1
提问by svenwltr
I am struggling with a REST application with Grizzly, Jersey and Hymanson, because Jersey ignores my custom ObjectMapper.
我正在努力使用 Grizzly、Jersey 和 Hymanson 的 REST 应用程序,因为 Jersey 忽略了我的自定义 ObjectMapper。
POM dependencies:
POM依赖:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.Hymanson.jaxrs</groupId>
<artifactId>Hymanson-jaxrs-json-provider</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>
Resulting versions are: Grizzly 2.3.3, Hymanson 2.1.4 and Jersey 2.2.
结果版本是:Grizzly 2.3.3、Hymanson 2.1.4 和 Jersey 2.2。
Main class (I want explicit registration of Jersey components):
主类(我想显式注册 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);
}
}
}
ContextResolver for ObjectMapper:
ObjectMapper 的 ContextResolver:
@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;
}
}
Neither ObjectMapperResolver
constructor nor getContext
get called. What am I missing? I would prefer to use Jersey 2.2 and Hymanson 2.1, because it is a dependency for another lib.
既不ObjectMapperResolver
构造函数也不getContext
被调用。我错过了什么?我更喜欢使用 Jersey 2.2 和 Hymanson 2.1,因为它是另一个库的依赖项。
A full example can be found on GitHub: https://github.com/svenwltr/example-grizzly-jersey-Hymanson/tree/stackoverflow
完整示例可以在 GitHub 上找到:https: //github.com/svenwltr/example-grizzly-jersey-Hymanson/tree/stackoverflow
采纳答案by svenwltr
I found a solution. I had to instantiate the Hymanson Provider by myself and set my custom ObjectMapper
. A working example can be found on GitHub: https://github.com/svenwltr/example-grizzly-jersey-Hymanson/tree/stackoverflow-answer
我找到了解决方案。我必须自己实例化 Hymanson Provider 并设置我的自定义ObjectMapper
. 可以在 GitHub 上找到一个工作示例:https: //github.com/svenwltr/example-grizzly-jersey-Hymanson/tree/stackoverflow-answer
I deleted my ObjectMapperResolver
and modified my main
method:
我删除了我的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
HymansonJaxbJsonProvider provider = new HymansonJaxbJsonProvider();
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);
}
}
}
回答by alexey
Please do this:
请这样做:
1) add pom.xml dependency
1) 添加 pom.xml 依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-Hymanson</artifactId>
<version>2.2</version>
</dependency>
2) register HymansonFeature in the Main.java
2) 在 Main.java 中注册 HymansonFeature
public class Main {
public static void main(String[] args) {
try {
ResourceConfig rc = new ResourceConfig();
rc.register(ExampleResource.class);
rc.register(ObjectMapperResolver.class);
rc.register(HymansonFeature.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) Use org.codehaus.Hymanson.map.ObjectMapper in your resource
3) 在你的资源中使用 org.codehaus.Hymanson.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.Hymanson.map.ObjectMapper;
import org.codehaus.Hymanson.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;
}
}
回答by krotscheck
I figured this out, based on a bit of tinkering.
我想通了这一点,基于一点修补。
The issue appears to be in Jersey's feature autodetection mechanism. If you rely on Jersey to load the HymansonJaxbJsonProvider, then the custom context provider for your ObjectMapper is ignored. If, instead, you manually register the feature, it works. I hypothesize that this has to do with the autodetected provider being loaded into a different context scope, but as for a solution, here's what I ended up with. Note that I wrapped it into a feature, you should be able to register it directly with your application without a problem.
问题似乎出在 Jersey 的功能自动检测机制中。如果您依赖 Jersey 加载 HymansonJaxbJsonProvider,那么您的 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(HymansonJaxbJsonProvider.class);
return true;
}
}
UPDATE November 2017: Things have changed a bit in the Jersey2 world. If the above doesn't work, try this:
2017 年 11 月更新:Jersey2 世界发生了一些变化。如果上述方法不起作用,请尝试以下操作:
The new method of providing your own ObjectMapper now looks like this:
提供您自己的 ObjectMapper 的新方法现在看起来像这样:
public final class HymansonFeature 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) {
HymansonJaxbJsonProvider provider = new HymansonJaxbJsonProvider(
MAPPER, DEFAULT_ANNOTATIONS);
context.register(provider);
return true;
}
}
回答by lathspell
As it took me some hours to get this working with Java EE7 and Glassfish4, here's my solution:
由于我花了几个小时才能使用 Java EE7 和 Glassfish4,这是我的解决方案:
@javax.ws.rs.ApplicationPath("withHymanson")
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 Hymanson JSON provider");
set.add(new HymansonJsonProvider().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.Hymanson.jaxrs.base.JsonMappingExceptionMapper.class);
resources.add(com.fasterxml.Hymanson.jaxrs.base.JsonParseExceptionMapper.class);
resources.add(com.fasterxml.Hymanson.jaxrs.json.JsonMappingExceptionMapper.class);
resources.add(com.fasterxml.Hymanson.jaxrs.json.JsonParseExceptionMapper.class);
resources.add(de.lathspell.java_test_ee7_json.Api.class);
resources.add(de.lathspell.java_test_ee7_json.with_Hymanson.MyExceptionMapper.class);
}
The only relevant POM dependencies are:
唯一相关的 POM 依赖项是:
<dependency>
<groupId>com.fasterxml.Hymanson.jaxrs</groupId>
<artifactId>Hymanson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.Hymanson.core</groupId>
<artifactId>Hymanson-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>
回答by Filip
The following solution applies to the following stack (as in... this is the setup I've used to test it)
以下解决方案适用于以下堆栈(如...这是我用来测试它的设置)
Jersey 2.12, Hymanson 2.4.x
泽西 2.12,Hyman逊 2.4.x
I'm adding my message w/ the solution I've come up with on this post since it was quite relevant for the many Google searches I've put in today... It is a cumbersome solution to what I believe to be an even more cumbersome problem.
我正在添加我的消息和我在这篇文章中提出的解决方案,因为它与我今天进行的许多 Google 搜索非常相关......这是一个繁琐的解决方案,我认为这是一个更麻烦的问题。
1. Make sure your maven configuration CONTAINS the Hymanson-jaxrs-json-provider
dependency:
1. 确保你的 maven 配置包含Hymanson-jaxrs-json-provider
依赖:
<dependency>
<groupId>com.fasterxml.Hymanson.jaxrs</groupId>
<artifactId>Hymanson-jaxrs-json-provider</artifactId>
<version>2.4.1</version>
</dependency>
2. Make sure your maven configuration DOESN'T CONTAIN the jersey-media-json-Hymanson
dependency:
2. 确保您的 Maven 配置不包含jersey-media-json-Hymanson
依赖项:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-Hymanson</artifactId>
</dependency>
3. Create a @Provider
component extending com.fasterxml.Hymanson.jaxrs.json.HymansonJaxbJsonProvider
like so:
3. 创建一个像这样@Provider
扩展的组件com.fasterxml.Hymanson.jaxrs.json.HymansonJaxbJsonProvider
:
import com.fasterxml.Hymanson.annotation.JsonAutoDetect;
import com.fasterxml.Hymanson.annotation.JsonInclude;
import com.fasterxml.Hymanson.annotation.PropertyAccessor;
import com.fasterxml.Hymanson.databind.DeserializationFeature;
import com.fasterxml.Hymanson.databind.ObjectMapper;
import com.fasterxml.Hymanson.databind.SerializationFeature;
import com.fasterxml.Hymanson.jaxrs.json.HymansonJaxbJsonProvider;
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 HymansonJaxbJsonProvider {
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);
}
}
As you can observe this is also where we define the custom instance of com.fasterxml.Hymanson.databind.ObjectMapper
正如您所观察到的,这也是我们定义自定义实例的地方 com.fasterxml.Hymanson.databind.ObjectMapper
4. Extend javax.ws.rs.core.Feature
via MarshallingFeature
like so:
4.扩展javax.ws.rs.core.Feature
通过MarshallingFeature
像这样:
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. You need to register this custom provider like so, provided you configure your application via org.glassfish.jersey.server.ResourceConfig
like so:
5. 您需要像这样注册这个自定义提供程序,前提是您通过org.glassfish.jersey.server.ResourceConfig
像这样配置您的应用程序:
import org.glassfish.jersey.server.ResourceConfig;
...
public class MyApplication extends ResourceConfig {
public MyApplication() {
...
register(MarshallingFeature.class);
...
}
}
Other notes and observations:
其他注意事项和观察:
- This solution applies whether you're using
javax.ws.rs.core.Response
to wrap your controller's responses or not. - Please make sure you carefully take into consideration (copy/paste) the following code snippets since the only "non-mandatory" so to speak bits are the ones regarding the custom configuration of the
com.fasterxml.Hymanson.databind.ObjectMapper
.
- 无论您是否使用
javax.ws.rs.core.Response
包装控制器的响应,此解决方案都适用。 - 请确保您仔细考虑(复制/粘贴)以下代码片段,因为唯一的“非强制性”可以说是有关
com.fasterxml.Hymanson.databind.ObjectMapper
.
@jcreason
@jcreason
Sorry for dropping the ball on this one @jcreason, I hope you're still curios. So I checked out the code from last year and this is what I came up w/ to provide a custom mapper.
很抱歉把球丢在这个@jcreason 上,我希望你仍然是好奇心。所以我检查了去年的代码,这就是我提供自定义映射器的想法。
The problem was that during feature initalization any custom object mappers get disabled by some code in
问题是在特性初始化期间,任何自定义对象映射器都会被某些代码禁用
org.glassfish.jersey.Hymanson.HymansonFeature:77 (jersey-media-json-Hymanson-2.12.jar)
org.glassfish.jersey.Hymanson.HymansonFeature:77 (jersey-media-json-Hymanson-2.12.jar)
// Disable other JSON providers.
context.property(PropertiesHelper.getPropertyNameForRuntime(InternalProperties.JSON_FEATURE, config.getRuntimeType()), JSON_FEATURE);
But this feature only gets registered by this component
但是这个功能只会被这个组件注册
org.glassfish.jersey.Hymanson.internal.HymansonAutoDiscoverable
org.glassfish.jersey.Hymanson.internal.HymansonAutoDiscoverable
if (!context.getConfiguration().isRegistered(HymansonFeature.class)) {
context.register(HymansonFeature.class);
}
So what I did was to register my own feature which registeres my own object mapper provider and drops in a trip wire stopping org.glassfish.jersey.Hymanson.HymansonFeature from being registered and overriding my object mapper...
所以我所做的是注册我自己的功能,该功能注册了我自己的对象映射器提供程序,并在绊线中阻止 org.glassfish.jersey.Hymanson.HymansonFeature 被注册并覆盖我的对象映射器......
import com.fasterxml.Hymanson.jaxrs.base.JsonMappingExceptionMapper;
import com.fasterxml.Hymanson.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(HymansonJsonProviderAtRest.class, MessageBodyReader.class, MessageBodyWriter.class);
final Configuration config = context.getConfiguration();
// Disables discoverability of org.glassfish.jersey.Hymanson.HymansonFeature
context.property(
PropertiesHelper.getPropertyNameForRuntime(InternalProperties.JSON_FEATURE,
config.getRuntimeType()), JSON_FEATURE);
return true;
}
}
And here is the custom object mapper provider...
这是自定义对象映射器提供程序...
import com.fasterxml.Hymanson.annotation.JsonAutoDetect;
import com.fasterxml.Hymanson.annotation.JsonInclude;
import com.fasterxml.Hymanson.annotation.PropertyAccessor;
import com.fasterxml.Hymanson.databind.DeserializationFeature;
import com.fasterxml.Hymanson.databind.ObjectMapper;
import com.fasterxml.Hymanson.databind.SerializationFeature;
import com.fasterxml.Hymanson.jaxrs.json.HymansonJaxbJsonProvider;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class HymansonJsonProviderAtRest extends HymansonJaxbJsonProvider {
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 HymansonJsonProviderAtRest() {
super();
setMapper(objectMapperAtRest);
}
}
回答by George
From the Jersey 2.17 docs: https://eclipse-ee4j.github.io/jersey.github.io/documentation/2.17/media.html#Hymanson-registration
来自 Jersey 2.17 文档:https: //eclipse-ee4j.github.io/jersey.github.io/documentation/2.17/media.html#Hymanson-registration
In the application
在申请中
@ApplicationPath("/")
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(HymansonFeature.class);
// This is the class that you supply, Call it what you want
register(HymansonObjectMapperProvider.class);
//...
}
}
Edit, forgot to add the HymansonObjectMapperProvider that you supply in register(..):
编辑,忘记添加您在 register(..) 中提供的 HymansonObjectMapperProvider:
import com.fasterxml.Hymanson.databind.DeserializationFeature;
import com.fasterxml.Hymanson.databind.ObjectMapper;
import com.fasterxml.Hymanson.databind.SerializationFeature;
import com.fasterxml.Hymanson.module.paramnames.ParameterNamesModule;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
@Provider
public class HymansonObjectMapperProvider implements ContextResolver<ObjectMapper>{
final ObjectMapper defaultObjectMapper;
public HymansonObjectMapperProvider() {
defaultObjectMapper = createDefaultMapper();
}
@Override
public ObjectMapper getContext(Class<?> type) {return defaultObjectMapper;}
public static ObjectMapper createDefaultMapper() {
final ObjectMapper Hymanson = 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
Hymanson.registerModule(new ParameterNamesModule());
Hymanson.enable(SerializationFeature.INDENT_OUTPUT);
Hymanson.disable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
Hymanson.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return Hymanson;
}
}
回答by c-toesca
With Hymanson 2.7, making this did not worked:
使用 Hymanson 2.7,这不起作用:
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(MyObjectMapperProvider.class);
}}
MyObjectMapperProvider constructor was called, but getContext() was never called.
调用了 MyObjectMapperProvider 构造函数,但从未调用过 getContext()。
Registering MyObjectMapperProvider in super() constructor make it work:
在 super() 构造函数中注册 MyObjectMapperProvider 使其工作:
public class MyApplication extends ResourceConfig {
public MyApplication() {
super(
// register Hymanson ObjectMapper resolver
MyObjectMapperProvider.class
);
}}
请参阅此 Jersey 示例代码。