【问题标题】:Jackson and JAX-RS: type resolving abstract types based on @PathParamJackson 和 JAX-RS:基于 @PathParam 的类型解析抽象类型
【发布时间】:2013-02-16 03:21:36
【问题描述】:

假设 Animal 是我项目中的一个抽象类,我有一个 REST 资源(在 JAX-RS 服务器上,使用 Jackson 进行(反)序列化)用于 PUT 来操作存储在我的数据库。它们有具体的类型,REST 资源在请求的路径中指定了类型:

@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{entityType}/{id: \\d+}")
public <T extends Animal> void putAnimal(@PathParam("entityType") String entityType, @PathParam("id") String id, Animal input) throws IOException {
    //...
}

我想使用entityType 来选择要反序列化的具体类(DogCat 或其他,因为entityTypedogcat 或其他)。由于此处无法解释的复杂原因,我无法将类型信息放入 JSON 输入本身。

所以 AIUI 用自定义 TypeIdResolver 或类似的东西注释 Animal 对我没有帮助,因为类型信息不会在 JSON 本身中(这就是类型解析器将获得的所有信息)。我打算使用自定义的MessageBodyReader,但据我所知,它没有从通过readValue 方法传递的主体中获取其他参数值,所以我不知道要反序列化成什么。

我错过了什么?如果这种方法失败,我如何在不指定特定于动物的端点的情况下完成我想要的(这会导致大量重复代码以及失去通用性 - 现在我可以添加Animal 的子类,这段代码将只是工作,非常好。)

【问题讨论】:

    标签: java jackson jax-rs


    【解决方案1】:

    鉴于您正在尝试做的事情,您最好构建一个包含基本方法的 AnimalResource 类,然后构建单独的 DogResourceCatResource 以及您需要的任何其他扩展 AnimalResource 的类。这将允许您获取Animal 的正确子类,从而正确反序列化输入 JSON。

    更新 如何实现这一点的一个例子。您的基础资源如下所示:

    public class AnimalResource<T extends Animal>
    {
        private final transient AnimalService<T> service;
    
        public AnimalResource(final AnimalService<T> service)
        {
            this.service = service;
        }
    
        @Get
        @Path("{id}")
        @Produces(MediaType.APPLICATION_JSON)
        public T getbyId(@PathParam("id") final String id)
        {
            return this.service.findById(id);
        }
    
        // Other CRUD methods go here
    }
    

    然后,假设它们都具有相同的设置,那么您的个体动物就是:

    @Path("/cats")
    public class CatResource extends AnimalResource<Cat>
    {
        public CatResource(final CatService catService)
        {
            super(catService);
        }
    }
    

    对于一只猫和

    @Path("/dogs")
    public class DogResource extends AnimalResource<Dog>
    {
        public DogResource(final DogService dogService)
        {
            super(dogService);
        }
    }
    

    对于狗。它们将从父级继承标准的 CRUD 方法,并且任何特定于动物的方法仍然可以放在单独的 *Resource 类中。

    【讨论】:

    • 正如我在问题中已经说过的,这将导致重复代码和失去一般性。你也没有具体说明为什么我这样做比我目前的方法更好。这在发生这种情况的项目中甚至也不实用,因为我们基本上将在运行时添加动物。在运行时添加 JAX-RS 资源并不好玩。
    • 首先,如果你有具体的子类,那么就不会有重复的代码,但这不是你的主要问题。根据您创建子类的方式(javassist?),您可能会也可能无法将它们与ObjectMapper 一起使用,但无论如何您都应该考虑构建过滤器。过滤器可以访问完整的请求、决定类型、解析输入并存储对象,以便您可以在资源中检索它。不过,这一切都将是相当手动的......
    • 如果有具体的子类,AIUI 你的方法将导致 1-1 对应的资源类,除了s/cat/dog/ 等之外,这将是相同的。这对我来说构成重复代码。是的,它的手动性是我试图避免的。 :-)
    • 这就是AnimalResource 的用武之地,您拥有所有基本方法,只需将其子类化为cat、dog 以及包含@Path 的任何内容。我会放一个简单的例子。
    • 对,所以这里CatResourceDogResource 是完全重复的,除了s/cat/dog/,这是我要避免的。
    【解决方案2】:

    以下来自 JAX-RS 规范(5.2.2 URI 和 URI 模板)的引用表明您应该能够将 UriInfo 实例注入您的自定义 MessageBodyReader,并使用其中一种方法来检查请求的 URL 路径。

    可以使用@Context 注解将 UriInfo 的实例注入到类字段或方法参数中。 UriInfo 提供有关请求 URI 组件的静态和动态的每个请求的信息。

    此处提供的示例显示了接收UriInfo 参数的资源方法,但通常也应该可以将实例注入提供程序(MessageBodyReader)。

    通过UriInfo获取entityType路径参数后,你的MessageBodyReader应该可以提供Animal对应的子类。

    【讨论】:

    • 经过测试,第一次完美运行。太棒了,非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 2011-10-10
    • 2020-06-16
    • 1970-01-01
    • 2011-07-22
    • 1970-01-01
    • 2020-04-05
    相关资源
    最近更新 更多