Java 如何在同一Jersey JAX-RS应用程序中使用两个版本的Jackson(1.x和2.x)?

Java 如何在同一Jersey JAX-RS应用程序中使用两个版本的Jackson(1.x和2.x)?,java,jackson,jersey,jax-rs,jersey-1.0,Java,Jackson,Jersey,Jax Rs,Jersey 1.0,我和老杰克逊(1.19.13)一起参与了一个旧版球衣(1.19.19)的项目。 我想切换到NewJackson(2.x)反序列化,但只针对新端点(以及逐渐的旧端点),因为一步迁移到Jackson 2和/或Jersey 2将非常困难(哦,monolith!) 我看过一些关于如何在Jersey的Jackson提供商中提供自定义配置的ObjectMapper,或者如何在Jersey的1.x版本中安装new Jackson的主题,但这不是我想要的,至少不是全部 我设想的解决方案是(最好)使用@UseJa

我和老杰克逊(1.19.13)一起参与了一个旧版球衣(1.19.19)的项目。 我想切换到NewJackson(2.x)反序列化,但只针对新端点(以及逐渐的旧端点),因为一步迁移到Jackson 2和/或Jersey 2将非常困难(哦,monolith!)

我看过一些关于如何在Jersey的Jackson提供商中提供自定义配置的ObjectMapper,或者如何在Jersey的1.x版本中安装new Jackson的主题,但这不是我想要的,至少不是全部

我设想的解决方案是(最好)使用@UseJackson2之类的东西来注释我的新JAX-RS端点,或者使用一些具有魔力的基类从正确的包中检索ObjectMapper,用于此特定端点并在以后扩展它——换句话说,强制给定端点使用其他(de)序列化提供程序比正常情况下更有效


我看到过不同配置的ObjectMapper的提供者示例(如这里),但在我的例子中,ObjectMapper将来自完全不同的包/maven工件

您可以创建一个处理2.x版本Jackson的
MessageBodyReader/Writer
isReadable
iswriteable
方法确定它可以处理哪些实体。您可以检查的是将Jersey的
extendedriinfo
注入提供程序,并检查
@Jackson2
注释的资源类。如果注释不存在,则您的提供程序将忽略该实体,运行时将转到下一个提供程序并检查其是否能够处理该实体;在本例中,是1.x提供程序

您只需扩展Jackson在it工件中已经提供的提供者,而不是完全创建自己的提供者

<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.9.2</version>
</dependency>
然后用
@Jackson2
注释您的资源类

早些时候,我试图让它工作时遇到了问题,因为我使用的是Jackson 2.8.4。整个2.8线都有问题。不确定是什么,但对于2.8,我的提供商根本不会注册。我使用次要版本2.2-2.9(不包括2.8)进行了测试,它们都可以工作


至于优先级,我不确定泽西岛是如何决定优先级的。如果首先调用1.x提供程序,那么这个解决方案将全部崩溃。解决这个问题的一种方法是使用组合而不是继承,您只需确定在
readFrom
writeTo
方法中使用哪个读写器(1.x或2.x)。x版本也是
JacksonJaxbJsonProvider
,但它使用了
codehaus
打包。根据我的测试,我的提供者总是首先被调用,所以这可能不需要。不幸的是,我无法确认这是出于设计。

基于Paul Samsotha的解决方案,Creain 2提供商对我来说效果很好

public class Main {
  public static void main(final String[] args) {
    ResourceConfig config = new ResourceConfig();
    config.register(Service1.class)
        .register(Service2.class)
        .register(JacksonV1Provider.class)
        .register(JacksonV2Provider.class);
    GrizzlyHttpServerFactory.createHttpServer(URI.create("http://0.0.0.0:8123"), config);
  }
}


我会尝试创建自己的MessageBodyReader/Writer,并使用它将调用委托给1.x阅读器/写入器或2.x阅读器/写入器。检查
注释[]
参数以查看您的
@UseJackson2
注释是否存在。这只是一个想法。不是我曾经尝试过的东西。或者你可以尝试扩展2.x读写器,覆盖isReadable和iswriteable并检查注释。请确保此提供程序的优先级高于1.x提供程序。不过,据我在这里所理解的(),annotations集合表示要反序列化的参数上的注释,而不是封闭的JAX-RS端点类。(例如,
void put(@Jackson2 TheType deserialize)
。所以代码很容易被污染,因为注释必须放在每个有效负载参数上。(以及如何将其应用于响应?)或者我确实得到了一些错误的结果。不管怎样,有趣的发现。有可能得到,让我们说一些’(de)serialization context',以了解反序列化来自哪个端点类?因此您可以将
@context extendedriinfo
注入读/写器中。您可以在那里获得所需的所有信息。必须尝试一下。关于优先级,此注释可能会成功?我还开始怀疑JacksonJaxb下一步不会调用扩展的JsonProvider?(不需要)它不应该被调用。它甚至不应该被注册,除非您使用的是类路径扫描。如果是,则提供程序将被注册,因为它用
@provider
注释。这是我看到它被注册的唯一方式。如果您使用的是类路径扫描,您应该改为仅包扫描。或者实际上(虽然我在测试时没有遇到过这种情况-可能只是类加载的差异),这可能是因为.Jersey会将它们用于提供程序。你可以尝试从jar中删除它们。是的,我刚刚测试过,这肯定是类加载(顺序)问题。我只是更改了我的(maven)依赖关系,我确实面临着同样的问题。最初我有1.x jersey son,但在jackson jaxrs json提供程序之后,我将它切换到了。是的,同样的问题。你只需要删除服务文件,这样它就不会自动注册。
public class Main {
  public static void main(final String[] args) {
    ResourceConfig config = new ResourceConfig();
    config.register(Service1.class)
        .register(Service2.class)
        .register(JacksonV1Provider.class)
        .register(JacksonV2Provider.class);
    GrizzlyHttpServerFactory.createHttpServer(URI.create("http://0.0.0.0:8123"), config);
  }
}
@Provider
public class JacksonV1Provider extends org.codehaus.jackson.jaxrs.JacksonJsonProvider {
  @Context
  private ExtendedUriInfo uriInfo;

  @Override
  public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return uriInfo.getPath().contains("/v1/");
  }

  @Override
  public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return uriInfo.getPath().contains("/v1/");
  }
}
@Provider
public class JacksonV2Provider extends com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider {
  @Context
  private ExtendedUriInfo uriInfo;

  @Override
  public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return uriInfo.getPath().contains("/v2/");
  }

  @Override
  public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return uriInfo.getPath().contains("/v2/");
  }
}
dependencies {
    compile group: 'org.glassfish.jersey.containers', name: 'jersey-container-grizzly2-http', version: '2.17'
    compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.17'

    compile group: "org.codehaus.jackson", name: "jackson-mapper-asl", version: "$jackson1_version"
    compile group: "org.codehaus.jackson", name: "jackson-jaxrs", version: "$jackson1_version"
    compile group: "org.codehaus.jackson", name: "jackson-xc", version: "$jackson1_version"

    compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jackson2_vervion"
    compile "com.fasterxml.jackson.core:jackson-core:$jackson2_vervion"
    compile "com.fasterxml.jackson.core:jackson-annotations:$jackson2_vervion"
}