【问题标题】:Jackson Provider Multiple ObjectMapper for same Class同一类的杰克逊提供者多个 ObjectMapper
【发布时间】:2017-07-23 10:17:08
【问题描述】:

我想用两个映射器在不同的资源方法中序列化同一个 Category 类。
我编写了两个以两种不同方式序列化 Category 的类 CategorySerializedCategoryTreeSerialized

public class MyJacksonJsonProvider implements ContextResolver<ObjectMapper>
{
    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        MAPPER.enable(SerializationFeature.INDENT_OUTPUT);          
        MAPPER.registerModule(new SimpleModule()
                .addSerializer(Category.class, new CategorySerializer(Category.class)));  
        }

    public MyJacksonJsonProvider() {
        System.out.println("Instantiate MyJacksonJsonProvider");
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        System.out.println("MyJacksonProvider.getContext() called with type: "+type);
        return MAPPER;
    }

这是简单的实体类别

   @Entity
   public class Category {

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)

        @Type(type = "objectid")
        private String id;
        private String name;

        @ManyToOne
        @JsonManagedReference
        private Category parent;

        @JsonBackReference
        @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
        @Column(insertable = false)
        private List<Category> children;

        ....getter and setter ....
    }

这是 CategoryResource

@Path(value = "resource")
public class CategoryResource {

    @Inject
    CategoryService categoryService;

    @Context
    Providers providers;

    @GET
    @Produces(value = MediaType.APPLICATION_JSON+";charset="+ CharEncoding.UTF_8)
    @Path("/categories")
    public List getCategories(){
        List<Category> categories = categoryService.findAll();
        return categories;
    }

    @GET
    @Produces(value = MediaType.APPLICATION_JSON+";charset="+ CharEncoding.UTF_8)
    @Path("/categoriestree")
    public List getCategoriesTree(){
        List<Category> categories = categoryService.findAll();

        ContextResolver<ObjectMapper> cr = providers
                .getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE);
        ObjectMapper c = cr.getContext(ObjectMapper.class);
        c.registerModule(new SimpleModule()
                .addSerializer(Category.class, new CategoryTreeSerializer(Category.class)));

        return categories;
    }

CategorySerialized 扩展 StdSerializer 已向提供程序注册

MAPPER.registerModule(new SimpleModule()
                    .addSerializer(Category.class, new CategorySerializer(Category.class))); 

CategoryTreeSerialized扩展StdSerializer在资源内注册

ContextResolver<ObjectMapper> cr = providers
                    .getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE);
            ObjectMapper c = cr.getContext(ObjectMapper.class);
            c.registerModule(new SimpleModule()
                    .addSerializer(Category.class, new CategoryTreeSerializer(Category.class)));

不幸的是,这不起作用,因为映射器是静态最终的。
调用的第一个资源,注册模块然后不改变

例如,如果我首先调用 /categoriestree 资源,我会得到 CategoryTreeSerialized 序列化。
但是,如果在我调用 /categories 资源后总是使用 CategoryTreeSerialized 类而不是 CategorySerialized 进行序列化

(反之亦然)

【问题讨论】:

  • 有道理。你有什么问题?
  • 我希望当我调用 getCategories (/categories) 时我会使用 CategorySerialized 进行序列化,而当我调用 getCategoryTree (/ categoriestree) 我使用 CategoryTreeSerialized 进行序列化
  • 所以使用不同的映射器。
  • 怎么样?在同一个提供商中?
  • 但是,无论在哪里。我看不出有什么问题。

标签: java json serialization jackson jackson-modules


【解决方案1】:

不确定这是否可能是 Spring MVC,我的示例是针对 JAX-RS,但通过谷歌搜索,您应该会找到类似的解决方案。 您可以为每个 Request 返回一个 Response,其中正文使用相应的序列化器进行序列化,例如:

@GET
@Produces(value = MediaType.APPLICATION_JSON+";charset="+ CharEncoding.UTF_8)
@Path("/categories")
public Response getCategories(){
    List<Category> categories = categoryService.findAll();
    ResponseBuilder response = ResponseBuilder.ok()
            .entity(new MyCategoriesMapper()
                .build(categories))
            .type(MediaType.APPLICATION_JSON));

    return response.build();
}

@GET
@Produces(value = MediaType.APPLICATION_JSON+";charset="+ CharEncoding.UTF_8)
@Path("/categoriestree")
public Response getCategoriesTree(){
    List<Category> categories = categoryService.findAll();
    ResponseBuilder response = ResponseBuilder.ok()
            .entity(new MyCategoriesTreeMapper()
                .build(categories))
            .type(MediaType.APPLICATION_JSON));

    return response.build();
}

【讨论】:

    【解决方案2】:

    我找到的唯一解决方案是创建一个包装类,该类复制 Category 类并注册模块

    我创建了一个名为 CategoryTree 的 Category 实体的副本,它是该实体的精确副本 我用 CategoryTreeSerialized 序列化注册了 CategoryTree 模块

    @Provider
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public class MyJacksonJsonProvider implements ContextResolver<ObjectMapper>
    {
        private static final ObjectMapper MAPPER_DEFAULT = new ObjectMapper();
    
    
        static {
           MAPPER_DEFAULT.enable(SerializationFeature.INDENT_OUTPUT);
           MAPPER_DEFAULT.registerModule(new SimpleModule()
                .addSerializer(Category.class, new CategorySerializer(Category.class)));
           MAPPER_DEFAULT.registerModule(new SimpleModule()
                .addSerializer(CategoryTree.class, new CategoryTreeSerializer(CategoryTree.class)));
    
        public MyJacksonJsonProvider() {
            System.out.println("Instantiate MyJacksonJsonProvider");
        }
    
        @Override
        public ObjectMapper getContext(Class<?> type) {
            System.out.println("MyJacksonProvider.getContext() called with 
            return MAPPER_DEFAULT;
        }
    }
    

    在 CategoryResource 类中,我将 List 的副本转换为 List

    @GET
    @Produces(value = MediaType.APPLICATION_JSON+";charset="+ CharEncoding.UTF_8)
    @Path("/categoriestree")
    public List getCategoriesTree(){
        List<Category> categories = categoryService.findAll();
    
        List<CategoryTree> categoryTrees =
                new CategoryTree().createList(categories);
        return categoryTrees;
    }
    

    那么 CategoryTree 是 Category 的一个副本
    这个解决方案不是很好执行和优雅,但它是唯一有效的解决方案,因为无法取消注册模块

    如果有人有不同的、执行的和优雅的解决方案,写下来

    【讨论】:

      猜你喜欢
      • 2015-12-05
      • 1970-01-01
      • 2019-04-16
      • 2021-01-09
      • 1970-01-01
      • 1970-01-01
      • 2021-01-23
      • 2017-12-22
      • 1970-01-01
      相关资源
      最近更新 更多