Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson mixin selection and inheritance

Tags:

jackson

I have a problem with Jackson mixin and inheritance. I have two target classes, a parent and a child. For those two target classes I have defined two MixIn classes (interfaces) with no inheritance relationship with each other. I also tested with one MixIn interface extending the other but there was no difference in the outcome. When Jackson serializes the parent class it uses the correctly defined mixin for the serialization config and everything works well. However when Jackson serializes the child class it will use the parent class mixin definitions for serializing properties that exist in both the parent and the child class. Then it uses the child class mixin definitions for serializing the properties defined in the child class but not in the parent class. Now this probably has something to do with comparing the base classes or implementing interfaces in Jackson.

Now the question is that is there any way that I could instruct Jackson to use only the mixin definitions for the child class when serializing objects of the child class? And yes I would like to keep both the the mixin definitions in place for two separate use cases so just removing the parent class mixin mapping is not gonna solve my issue.

Example code and expected and actual output JSONs below.

Environment: Jackson version 2.1.4 Tomcat version 7.0.34.0

Target classes and interfaces they implement:

public interface TestI {

  public String getName();  
}


public interface TestExtendI extends TestI {

   public Integer getAge(); 
}


public class Test implements TestI {

  String name;

  public Test(String name) {
    this.name = name;
  }

  @Override
  public String getName() {
    return name;
  }  
}

public class TestExtend extends Test implements TestExtendI {

  private Integer age;

  public TestExtend(String name) {
    super(name);
  }

  public TestExtend(String name, Integer age) {
    super(name);
    this.age = age;
  }

  @Override
  public Integer getAge() {
    return age;
  }
}

Mixins definitions

public interface TestMixIn {

  @JsonProperty("base-name")
  public String getName();
}


public interface TestExtendMixIn {

  @JsonProperty("ext-name")
  public String getName();

  @JsonProperty("ext-age")
  public Integer getAge();
}

If both mixins are added to the mapper the output JSON is:

{
  "base-name": "5", // from parent class mixin definition
  "ext-age": 50     // from child class mixin defition
}

With mixin for TestI.class commented everything works as expected and the output JSON is (this is what I would like to achieve):

{
  "ext-name": "5",  // from child class mixin defition
  "ext-age": 50     // from child class mixin defition
}

Object mapper configuration

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JacksonObjectMapper implements ContextResolver<ObjectMapper> {

  private ObjectMapper mapper;

  public JacksonObjectMapper() {
    mapper = new ObjectMapper();
    mapper.addMixInAnnotations(TestI.class, TestMixIn.class);
    mapper.addMixInAnnotations(TestExtendI.class, TestExtendMixIn.class);    
  }  

  public ObjectMapper getContext(Class<?> type) { 
    return this.mapper; 
  }
}

REST api for handling the request/response

@Path("api/test/{id}")
public class TestRestApi {
  @GET
  @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
  public TestI getTest(@PathParam("id") String id) {

    TestI ret = new TestExtend(id, 50);    
    return ret;
  }
}

Solution

As described by pgelinas in the first response the solution to this problem is to define the methods that should be handled by the 'child' mixin again in the child interface. For the example code above that would mean changes to the TestExtendI interface:

public interface TestExtendI extends TestI {

  public Integer getAge();

  // override the method from the parent interface here
  @Override
  public String getName();
}

This will solve the issue and doesn't add too much boilerplate code to the solution. Moreover it will not change the interface contracts since the child interface already extends the parent interface.

like image 656
user2239050 Avatar asked Apr 03 '13 06:04

user2239050


1 Answers

This is a tricky one; the answer to your specific question is no, you cannot tell a child class to not use the Mixin applied to a parent class.

However, a simple solution to your problem here is to re-declare the getName() method in the TestExtendI interface. I believe MixIn annotation resolution doesn't follow the usual parent-child override (as is the case with normal annotations), but will instead prefer the MixIn that is applied to the class that declares the method. This might be a bug in Jackson or a design choice, you can always fill an issue on github.

like image 119
Pascal Gélinas Avatar answered Jan 01 '23 11:01

Pascal Gélinas