1

I want to serialize the same Category class with two mapper in different resources method.
I have written two classes that serialized Category in two different ways CategorySerialized and 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;
    }

this is the simple entity Category

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

this is the 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 is registered with the provider

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

CategoryTreeSerialized extends StdSerializer is registered within the resources

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

Unfortunately this does not work because mapper is static final.
The first resource called, register the module and then does not change

For example if I call the /categoriestree resource first, I get CategoryTreeSerialized serialization.
But if after I call the /categories resource is always serialized with the CategoryTreeSerialized class and not with CategorySerialized

(And vice versa)

Pako
  • 339
  • 2
  • 8
  • 20
  • Makes sense. What's your question? – shmosel Jul 23 '17 at 10:20
  • I want that when i call **getCategories** (/categories) i get serialization with **CategorySerialized**, and when i call **getCategoryTree** (/ categoriestree) i get serialization with **CategoryTreeSerialized** – Pako Jul 23 '17 at 10:27
  • So use a different mapper. – shmosel Jul 23 '17 at 10:30
  • how? In the same provider? – Pako Jul 23 '17 at 10:55
  • However, wherever. I'm not seeing what the trouble is. – shmosel Jul 23 '17 at 11:02
  • Perfect I create more mapper (MAPPER_CATEGORY and MAPPER_CATEGORY_TREE) but when i call getContext(Class> type) i can to check if Category.class and return the mapper of class Category which can only be one because the class Category is the same when register module – Pako Jul 23 '17 at 11:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149924/discussion-between-pako-and-shmosel). – Pako Jul 23 '17 at 11:40

2 Answers2

1

Not sure if this could be Spring MVC, my example is for JAX-RS, but with googling you should find a similar Solution. You could return a Response for every Request, where the body is serialized in with the corresponding Serializer like:

@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();
}
sschrass
  • 7,014
  • 6
  • 43
  • 62
0

The only solution I have found is to create a wrapper class that makes a copy of the Category class and register the module

I've created a copy of the Category entity called CategoryTree which is an exact copy of the entity I registered the CategoryTree module with CategoryTreeSerialized serialization

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

In the CategoryResource class I get a copy of the List transformed into 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;
}

Then CategoryTree is a copy of Category
This solution is not very performing and elegant, but it is the only one that works because it is not possible to unregister the module

If someone has a different, performing and elegant solution write it down

Pako
  • 339
  • 2
  • 8
  • 20