Search code examples
javajacksonjackson-databind

Set Type Discriminator programmatically in Jackson


For polymorphic deserialisation, Jackson's ObjectMapper wants to know:

  1. Which Subtypes are there to consider
  2. How to decide, which Subtype to use

There are some standard ways, using fully qualified class names and certain reserved JSON properties, so Jackson can deduct those things without further configuration.

Another common way is, to provice Jackson with the necessary infromation by adding the Annotations @JsonTypeInfo and @JsonSubtypes to the base type. This however implies, that the file, declaring the base class has to be modified, when ever a new subtype is added.

It is also possible to regiser subtypes to the ObjectMapper programmatically at runtime via objectMapper.registerSubtypes(...).

Now I am looking for a way to also provide the information from @JsonTypeInfo programmatically at runtime without using that annotation.

Somthing like objectMapper.addTypeInfo(new TypeInfo(BaseType.class, PROPERTY, "myPropertyName", NAME); so I can use polymorphic deserialization on types that are declared in another project, that knows nothing of Jackson or any of its annotations.


Solution

  • To register the @JsonTypeInfo without modifying the actual class you have to use a mixin like this:

    // actual base type that we don't want to or can't modfiy 
    // because it is in a different module / 3rd party
    public class BaseType {  
        ...
    }
    
    // mixin for BaseType to define @JsonTypeInfo
    // this can be in a completely different package / module
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.PROPERTY, property = "type")
    public abstract class BaseTypeMixIn {
    }
    

    The mixin must be registered manually for the ObjectMapper:

    objectMapper.addMixIn(BaseType.class, BaseTypeMixIn.class);
    

    BaseType now effectively has the @JsonTypeInfo from the mixin. Mixin in jackson are the general solution to the problem "How do I annotate a class that I can't modify".

    For sub-types the type info can be registered with ObjectMapper.registerSubtypes or by annotating the mixin with @JsonSubtypes. I prefer doing in without annotation in this case because it also works if different modules have different sub types of the base type. Registering multiple mixins will most likely not work.

    public class SubTypeA extends BaseType {
       ...
    }
    
    public class SubTypeB extends BaseType {
        ...
    }
    

    Register in the ObjectMapper:

    objectMapper.registerSubtypes(
       new NamedType(SubTypeA.class, "A"), 
       new NamedType(SubTypeB.class, "B"));