Search code examples
javajsonjacksongsonbidirectional

Working with bi-dirctional JACKSON


first, sorry about my bad english;

Second, I have the following Code:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")    

public class UserAccount implements Serializable  {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private List<Venda> vendas;

    }

And the following:

public class Venda implements Serializable  {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private UserAccount cliente;

    }

So, everything is okay and get the json from serialize on this way (when I ask for an UserAccount):

[
  {
    "id": 1,    
    "vendas": [
      {
        "id": 1,        
        "cliente": 1,        
      }
    ]
  }
]

And when I ask for a Venda:

[
  {
    "id": 1,    
    "cliente": {
      "id": 1,      
      "vendas": [
        {
          "id": 1,        
          "cliente": 1         
        }
      ]
    }
  }
]

The problem is, I don't need the "cliente" information on "vendas" in the first case, but in the second one I need the "cliente" information, However I don't want his "vendas", cause I already got it before;

I already trid @JsonIgnore and didn't work for me, what should I do?

PS: I'm working with GSON to get the .Class from JSON, and I get a terrible Exception because sometimes cliente is an Object and sometimes is Integer, so if you guys have another solution that makes cliente and vendas don't change their type, i would to know too. :(


Solution

  • I was able to solve this using Jackson's Mix-in feature. The Mixin feature is a class were you can specify json annotations (on the class, fields and getters/setters) and they apply to the bean/pojo you serialize. Basically, a mixin allows adding annotations at run time and without chaning the bean/pojo source file. You use Jackson's module feature to apply a Mixin at run time.

    So I created one mixin that dynamically adds @JsonIgnore annotation to vendas getter method of UserAccount class, and another mixin that adds @JsonIgnore annotation to cliente getter method of Venda class.

    Here is the modified UserAccount class:

    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    public class UserAccount implements Serializable
    {
        private static final long serialVersionUID = 1L;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private List<Venda> vendas = new ArrayList<>();
    
        public Long getId() { return id; }
        public void setId(Long id) { this.id = id; }
        public List<Venda> getVendas() { return vendas; }
        public void        setVendas(List<Venda> vendas) { this.vendas = vendas; }
        public void        addVenda(Venda v) { 
            this.vendas.add(v);
            v.setCliente(this);
        }
    
        /**
         * a Jackson module that is also a Jackson mixin 
         * it adds @JsonIgnore annotation to getVendas() method of UserAccount class
         */
        public static class FilterVendas extends SimpleModule {
            @Override
            public void setupModule(SetupContext context) {
                context.setMixInAnnotations(UserAccount.class, FilterVendas.class);
            }
            // implementation of method is irrelevant. 
            // all we want is the annotation and method's signature 
            @JsonIgnore
            public List<Venda> getVendas() { return null; }  
        }
    

    Here is the modified Venda class:

    public class Venda implements Serializable
    {
        private static final long serialVersionUID = 1L;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private UserAccount cliente;
    
        public Long getId() { return id; }
        public void setId(Long id) { this.id = id; }
        public UserAccount getCliente() { return cliente; }
        public void        setCliente(UserAccount cliente) { this.cliente = cliente; }
    
        /**
         * a Jackson module that is also a Jackson mixin 
         * it adds @JsonIgnore annotation to getCliente() method of Venda class
         */
        public static class FilterCliente extends SimpleModule {
            @Override
            public void setupModule(SetupContext context) {
                context.setMixInAnnotations(Venda.class, FilterCliente.class);
            }
            // implementation of method is irrelevant. 
            // all we want is the annotation and method's signature 
            @JsonIgnore
            public UserAccount getCliente() { return null; }
        }
    }
    

    and the test method with run time object mapper configuration:

    public static void main(String... args) {
        Venda v = new Venda();
        UserAccount ua = new UserAccount();
        v.setId(1L);
        ua.setId(1L);
        ua.addVenda(v);
        try {
            ObjectMapper mapper = new ObjectMapper();
            System.out.println("UserAccount: (unfiltered)");
            System.out.println(mapper.writeValueAsString(ua));
    
            mapper = new ObjectMapper();
            // register module at run time to apply filter
            mapper.registerModule(new Venda.FilterCliente());
            System.out.println("UserAccount: (filtered)");
            System.out.println(mapper.writeValueAsString(ua));
    
            mapper = new ObjectMapper();
            System.out.println("Venda: (unfiltered)");
            System.out.println(mapper.writeValueAsString(v));
    
            mapper = new ObjectMapper();
            // register module at run time to apply filter
            mapper.registerModule(new UserAccount.FilterVendas());
            System.out.println("Venda: (filtered)");
            System.out.println(mapper.writeValueAsString(ua));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    output:

    UserAccount: (unfiltered)
    {"id":1,"vendas":[{"id":1,"cliente":1}]}
    UserAccount: (filtered)
    {"id":1,"vendas":[{"id":1}]}
    Venda: (unfiltered)
    {"id":1,"cliente":{"id":1,"vendas":[{"id":1,"cliente":1}]}}
    Venda: (filtered)
    {"id":1}