自定义对象映射器与泽西岛 2.2 和杰克逊 2.1

2022-08-31 20:26:31

我正在与Grizzly,Jersey和Jackson一起使用REST应用程序,因为Hire忽略了我的自定义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>

结果版本是:灰熊2.3.3,杰克逊2.1.4和泽西2.2。

主类(我想要泽西岛组件的显式注册):

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:

@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;
    }

}

既不是构造函数,也不是被调用。我错过了什么?我更喜欢使用泽西岛2.2和Jackson 2.1,因为它是另一个库的依赖项。ObjectMapperResolvergetContext

可以在GitHub上找到一个完整的例子:https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow


答案 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. 创建一个扩展组件,如下所示:@Providercom.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

4. 通过以下方式扩展:javax.ws.rs.core.FeatureMarshallingFeature

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);
        ...
     }
 }

其他注意事项和意见:

  1. 无论是否用于包装控制器的响应,此解决方案都适用。javax.ws.rs.core.Response
  2. 请确保您仔细考虑(复制/粘贴)以下代码片段,因为唯一的“非强制性”位是关于 的自定义配置的位。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);
    }
}

答案 2

我找到了一个解决方案。我不得不自己实例化杰克逊提供程序并设置我的自定义。可以在GitHub上找到一个工作示例:https://github.com/svenwltr/example-grizzly-jersey-jackson/tree/stackoverflow-answerObjectMapper

我删除了我的方法并修改了我的方法:ObjectMapperResolvermain

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);
        }
    }
}

推荐