Jackson Provider多个ObjectMapper用于同一类

时间:2017-07-23 10:17:08

标签: java json serialization jackson jackson-modules

我想用不同的资源方法用两个mapper序列化相同的Category类 我写过两个以两种不同方式序列化Category的类 CategorySerialized CategoryTreeSerialized

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

不幸的是,这不起作用,因为mapper是静态的final 调用第一个资源,注册模块然后不更改

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

(反之亦然)

2 个答案:

答案 0 :(得分:1)

不确定这可能是Spring MVC,我的例子是JAX-RS,但是使用Google搜索你应该找到类似的解决方案。 您可以为每个Response返回一个Request,其中正文与相应的序列化程序序列化,如:

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

答案 1 :(得分:0)

我找到的唯一解决方案是创建一个包装类,它创建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是类别
的副本 这个解决方案性能不是很优雅,但它是唯一可行的解​​决方案,因为无法取消注册模块

如果某人有不同的,表现优雅的解决方案,请将其写下来