自定义ObjectMapper与泽西岛2.2和jackson2.1
由于Jersey忽略了我的自定义ObjectMapper,因此我正在与Grizzly,Jersey和Jackson一起使用REST应用程序。
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>
最终的版本是:灰熊2.3.3,jackson2.1.4和泽西岛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的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; } }
ObjectMapperResolver
构造函数和getContext
都不会被调用。 我错过了什么? 我宁愿使用Jersey 2.2和Jackson 2.1,因为它是另一个lib的依赖。
一个完整的例子可以在GitHub上find: https : //github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow
以下解决scheme适用于以下堆栈(如…这是我用来testing它的设置)
泽西岛2.12,jackson2.4.x
我在这篇文章中添加了我的信息,这个解决scheme与我在今天提出的Googlesearch很相关…这对于我认为是一个麻烦的解决scheme来说是一个麻烦的解决scheme更麻烦的问题。
1.确保您的mavenconfiguration包含jackson-jaxrs-json-provider
依赖项:
<dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-json-provider</artifactId> <version>2.4.1</version> </dependency>
2.确保你的mavenconfiguration不包含jersey-media-json-jackson
依赖:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> </dependency>
3.创build一个扩展com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider
的@Provider
组件, com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider
所示:
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
的自定义实例的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
configuration你的应用程序就像这样:
import org.glassfish.jersey.server.ResourceConfig; ... public class MyApplication extends ResourceConfig { public MyApplication() { ... register(MarshallingFeature.class); ... } }
其他说明和观察:
- 无论您是使用
javax.ws.rs.core.Response
封装控制器的响应,此解决scheme都适用。 - 请确保您仔细考虑(复制/粘贴)下面的代码片段,因为唯一的“非强制性”这么说的位是关于
com.fasterxml.jackson.databind.ObjectMapper
的自定义configuration。
@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);
但是这个function只能被这个组件注册
org.glassfish.jersey.jackson.internal.JacksonAutoDiscoverable
if (!context.getConfiguration().isRegistered(JacksonFeature.class)) { context.register(JacksonFeature.class); }
所以我做的是注册我自己的function,注册我自己的对象映射器提供程序,并在一个旅行线停下来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); } }
我find了解决scheme。 我必须自己实例化Jackson提供程序,并设置自定义的ObjectMapper
。 一个可用的例子可以在GitHub上find: https : //github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow-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); } } }
请这样做:
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; } }
基于一点修补,我明白了这一点。
这个问题似乎在泽西岛的function自动检测机制。 如果您依赖Jersey来加载JacksonJaxbJsonProvider,那么ObjectMapper的自定义上下文提供程序将被忽略。 相反,如果您手动注册该function,则可以使用。 我假设这与自动检测的提供程序被加载到不同的上下文范围有关,但是对于一个解决scheme,这是我最后的结果。 请注意,我把它包装成一个function,你应该能够直接注册它与您的应用程序没有问题。
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; } }
由于花了我几个小时才能使用Java EE7和Glassfish4,下面是我的解决scheme:
@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>
从泽西岛2.17文档: https : //jersey.java.net/documentation/latest/user-guide.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); //... } }
编辑,忘记添加您在注册(..)中提供的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; } }
与jackson2.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 ); }}
看到这个泽西岛示例代码 。