3

I have a POJO to be serialized which has field of type Object named representation and I have a custom serializer written for it.

POJO:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "user", propOrder = {
    //......
})
public class User {

    protected User.Event userEvent;
    protected Boolean isValid;
    protected Boolean isPrivleged;

    // getter/ setters

     // Inner static class
     public static class Event {

        protected Object representation;
        protected User.Event.Monitor userMonitor;

        // getter setters and Monitor static class

     }
}

Now, I cannot edit my POJO for some reason, so I want all configurations of Jackson in code via ObjectMapper. I am not able to register my custom serializer for field Object representation as it handles type Object which is a super class for all.

    public class CustomSerializer extends JsonSerializer<Object>{

        @Override
        public void serialize(Object obj, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {



            jgen.writeObject(content);
            // ...........      
        }

        @Override
        public Class<Object> handledType() {
            return Object.class;
        }

    }

This gives exception:

java.lang.IllegalArgumentException: JsonSerializer of type CustomSerializer does not define valid handledType() -- must either register with method that takes type argument  or make serializer extend 'com.fasterxml.jackson.databind.ser.std.StdSerializer'
    at com.fasterxml.jackson.databind.module.SimpleSerializers.addSerializer(SimpleSerializers.java:80)
    at com.fasterxml.jackson.databind.module.SimpleModule.addSerializer(SimpleModule.java:240)

So I guess since every field has superclass Object, thus it's saying invalid handleType().

Is there a way to register a Serializer programtically via the fieled name or something. Eg When field name is representation register my serializer for it ??

How to handle the above case ??

Siddharth Trikha
  • 2,648
  • 8
  • 57
  • 101

2 Answers2

2

Have you ever considered Jackson mix-in annotations?

Jackson mix-in annotations

It's a great alternative when modifying the classes is not an option. You can think of it as kind of aspect-oriented way of adding more annotations during runtime, to augment statically defined ones.

Define a mix-in annotation interface (class would do as well):

public interface EventMixIn {

    @JsonProperty("representation")
    @JsonSerialize(using = CustomSerializer.class)
    Object getRepresentation();
}

Then configure ObjectMapper to use the defined interface as a mix-in for your POJO:

ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT)
                                        .addMixIn(User.Event.class, EventMixIn.class); 

Usage considerations

Here are some usage considerations:

  • All annotation sets that Jackson recognizes can be mixed in.
  • All kinds of annotations (member method, static method, field, constructor annotations) can be mixed in.
  • Only method (and field) name and signature are used for matching annotations: access definitions (private, protected, ...) and method implementations are ignored.

For more details, you can have a look at this page.

cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • Thanks for the detailed answer. i tried as it is in above, but somehow my custom serializer is not getting called (I added an SOP in it, it was not printed). PS: I am using Jackson 2.x so to add MixIn i use: `SimpleModule simpleModule = new SimpleModule("SimpleModule"); simpleModule.setMixInAnnotation(User.Event.class, EventMixIn.class); objectMapper.registerModule(simpleModule);` – Siddharth Trikha Aug 11 '16 at 09:24
  • @SiddharthTrikha I've just tried both approaches to add the mix-in and both worked for me. The custom serializer is invoked as expected here. – cassiomolin Aug 11 '16 at 09:30
  • 1
    I was also using `JaxbAnnotationIntrospector`, thus my serializer was not being called. Thanks – Siddharth Trikha Aug 11 '16 at 09:57
  • @Casio: Can I add one MixIn class for the above case ?? Like UserMixIn which also holds annotations for it's subclass Event ?? Or do I have to add separate MixIns ? – Siddharth Trikha Aug 11 '16 at 11:27
  • 1
    @SiddharthTrikha According to the [documentation](https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations), *mix-ins work as expected within inheritance hierarchy: it is feasible (and useful) to attach mix-in annotations to super-classes -- if so, mix-in annotations can further be overridden by annotations sub-classes (of target) provide*. – cassiomolin Aug 11 '16 at 11:48
1

Just to add to the excellent answer by cassiomolin if you're trying to use this with Spring Boot and Kotlin to use your custom Jackson ObjectMapper:

// the custom serializer
class CustomSerializer : JsonSerializer<Any>() {
  override fun serialize(value: Any?, 
                         gen: JsonGenerator?, 
                         serializers: SerializerProvider?) {
    gen?.let { 
      gen.writeObject(content) 
     
      // ...
    }
  }
}

// the mixin
interface EventMixIn {
  @JsonProperty("representation")
  @JsonSerialize(using = CustomSerializer::class)
  fun getRepresentation(): Any?
}

// the config with the bean
@Configuration
class AppConfig {
  @Bean
  fun createObjectMapper(): MappingJackson2HttpMessageConverter {
    val objectMapper = ObjectMapper()
    objectMapper.addMixIn(Event::class.java, EventMixIn::class.java)
    return MappingJackson2HttpMessageConverter(objectMapper)
  }
}
Dexter Legaspi
  • 3,192
  • 1
  • 35
  • 26