Search code examples
javajacksonannotationsmixinsjackson-databind

How can I get a Jackson mixin to work with private fields?


I was experimenting with Jackson 2.0 mixins to serialize a class with no annotations.

Simplified source code below. Note that I'm not using getters/setters, but it seemed like I should still be able to use mixins according to the documentation.

public class NoAnnotation {
   private Date created;
   private String name;

   // make one with some data in it for the test
   static NoAnnotation make() {
      NoAnnotation na= new NoAnnotation();
      na.created = new Date();
      na.name = "FooBear";
      return na;
   }

   // my Mixin "class"
   static class JacksonMixIn {
      JacksonMixIn(@JsonProperty("created") Date created,
                   @JsonProperty("name") String name)
         { /* do nothing */ }
   }

   // test code
   public static void main(String[] args) throws Exception {
      NoAnnotation na = NoAnnotation.make();
      ObjectMapper objectMapper = new ObjectMapper();
      objectMapper.addMixInAnnotations(NoAnnotation.class, JacksonMixIn.class);
      String jsonText = objectMapper.writeValueAsString(na);
      System.out.println(jsonText);
   }
}

When I run main I get

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class com.flyingspaniel.so.NoAnnotation and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.SerializationFeature.FAIL_ON_EMPTY_BEANS) )
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:51)
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:25)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:108)
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:2407)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:1983)
    at com.flyingspaniel.so.NoAnnotation.main(NoAnnotation.java:49)

When I follow the instructions in the Exception and add a line

objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

I no longer get an exception, but the result is an empty JSON object, {}.

If I make the fields public it works, but that is not something I want to do, as it's not a reasonable object design.

I'm guessing that I am leaving out a basic "setThis" step somewhere, but don't know what. How can I get mixins to work in this situation?


Solution

  • I figured it out. If you want to access private fields, you need to play with the Visibility by adding the following line:

    objectMapper.setVisibilityChecker(VisibilityChecker.Std.defaultInstance()
            .withFieldVisibility(Visibility.ANY));
    

    For protected fields, you could also use Visibility.PROTECTED_AND_PUBLIC.

    Full example

    // test code
    public static void main(String[] args) throws Exception {
       NoAnnotation na = NoAnnotation.make();
       ObjectMapper objectMapper = new ObjectMapper();
       objectMapper.addMixInAnnotations(NoAnnotation.class, JacksonMixIn.class);
       objectMapper.setVisibilityChecker(VisibilityChecker.Std.defaultInstance()
               .withFieldVisibility(Visibility.ANY));
       String jsonText = objectMapper.writeValueAsString(na);
       System.out.println(jsonText);
    }