我正在努力使用Grizzly、泽西和Jackson的REST应用程序,因为泽西忽略了我的自定义ObjectMapper。
聚甲醛依赖关系:
<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和泽西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);
}
}
}
对象映射器的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上找到一个完整的示例:https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow
以下解决方案适用于以下堆栈(如...这是我用来测试它的设置)
我正在添加我的信息,其中包含我在这篇文章中提出的解决方案,因为它与我今天输入的许多 Google 搜索非常相关......这是一个繁琐的解决方案,我认为这是一个更加繁琐的问题。
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
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. firerxml.jackson.data.ObjectMapper
的自定义实例的地方
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;
}
}
import org.glassfish.jersey.server.ResourceConfig;
...
public class MyApplication extends ResourceConfig {
public MyApplication() {
...
register(MarshallingFeature.class);
...
}
}
其他说明和意见:
javax.ws.rs.core.Respons
来包装控制器的响应,此解决方案都适用com.fasterxml.jackson.databind.ObjectMapper
的自定义配置的位很抱歉把球丢在了这个@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.玻璃鱼。泽西。杰克逊。内部。杰克逊汽车发现
if (!context.getConfiguration().isRegistered(JacksonFeature.class)) {
context.register(JacksonFeature.class);
}
所以我所做的是注册我自己的功能,它注册了我自己的对象映射器提供程序,并放入一个绊线,阻止org.lassfish.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);
}
}
我找到了一个解决方案。我必须自己实例化Jackson Provider,并设置自定义的<code>ObjectMapper</code>。在GitHub上可以找到一个工作示例:https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow-answer
我删除了我的< code > ObjectMapperResolver 并修改了我的< code>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的功能自动检测机制上。如果您依赖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世界的情况发生了一些变化。如果以上方法不起作用,请尝试以下操作:
提供您自己的对象映射器的新方法现在如下所示:
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;
}
}