6

In Jackson, I am using annotation @JsonTypeInfo to include polymorphism support.

If, I do not want to go with annotation based approach, I can use global default typing or override the type information handling module.

I have tried global type information but it is emitting type information for all non final type.

What I need ,

  1. I want to include type information only for polymorphic type.
  2. I want to change default format of type info (to key-value pair)

Is it possible to achieve above two points just by twitting global configuration?

If not, what extension point should I used used to customize type-information module ? I have read JacksonAnnotationIntrospector is the class which deals with type info.

Should I customize it to achieve above mentioned two points?

Help with Example will be well and good.

Ketan
  • 2,612
  • 5
  • 31
  • 44

3 Answers3

14

You can use Jackson's DefaultTypeResolverBuilder for this purpose. Extend this class and override the useForType method appropriately. Here is an example that adds type information only for the classes belonging to the test.jackson package (and sub-packages):

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTypeResolverBuilder;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;

public class CustomTypeResolverBuilder extends DefaultTypeResolverBuilder
{
    public CustomTypeResolverBuilder()
    {
        super(DefaultTyping.NON_FINAL);
    }

    @Override
    public boolean useForType(JavaType t)
    {
        if (t.getRawClass().getName().startsWith("test.jackson")) {
            return true;
        }

        return false;
    }
}

Now, consider that you have Foo.java in test.jackson package and Bar.java in org.myorg package, each containing an int variable called "integer" and a String variable called "string".

You can serialize objects of these two classes this way:

ObjectMapper objectMapper = new ObjectMapper();

TypeResolverBuilder<?> typeResolver = new CustomTypeResolverBuilder();
typeResolver.init(JsonTypeInfo.Id.CLASS, null);
typeResolver.inclusion(JsonTypeInfo.As.PROPERTY);
typeResolver.typeProperty("@CLASS");
objectMapper.setDefaultTyping(typeResolver);

Foo foo = new Foo(10, "Foo");
Bar bar = new Bar(20, "Bar");

System.out.println(objectMapper.writeValueAsString(foo));
System.out.println(objectMapper.writeValueAsString(bar));

The corresponding output will be:

{"@CLASS":"test.jackson.Foo","integer":10,"string":"Foo"}
{"integer":20,"string":"Bar"}

You can also customize the name of the attribute that represents the type ("@CLASS" in the above example). Hope this helps!

Jackall
  • 1,120
  • 1
  • 9
  • 18
  • [JacksonPolymorphicDeserialization · FasterXML/jackson-docs Wiki](https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization) – Jason Law Jul 10 '20 at 03:12
0

You can use the Moonwlker library.

With it, you can create an ObjectMapper like this:

 ObjectMapper objectMapper = new ObjectMapper();

 MoonwlkerModule module =
   MoonwlkerModule.builder()
     .fromProperty("@CLASS").toSubclassesOf(Animal.class)
     .build();

 objectMapper.registerModule(module);

And then use that mapper to (de)serialize. The Moonwlker website contains more details and configuration options.

bertilmuth
  • 276
  • 1
  • 11
0
private static final ObjectMapper objectMapper = new ObjectMapper() {{
    this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    this.setVisibility(this.getSerializationConfig()
            .getDefaultVisibilityChecker()
            .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
            .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
            .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
            .withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
    this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    this.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
    this.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
    this.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
    this.addMixIn(Throwable.class, ThrowableMixIn.class);
    TypeResolverBuilder<?> mapTyper = new DefaultTypeResolverBuilder(DefaultTyping.NON_FINAL) {
        public boolean useForType(JavaType t) {
            switch (_appliesFor) {
                case NON_CONCRETE_AND_ARRAYS:
                    while (t.isArrayType()) {
                        t = t.getContentType();
                    }
                    // fall through
                case OBJECT_AND_NON_CONCRETE:
                    return (t.getRawClass() == Object.class) || !t.isConcrete();
                case NON_FINAL:
                    while (t.isArrayType()) {
                        t = t.getContentType();
                    }
                    // to fix problem with wrong long to int conversion
                    if (t.getRawClass() == Long.class) {
                        return true;
                    }
                    if (t.getRawClass() == XMLGregorianCalendar.class) {
                        return false;
                    }
                    return !t.isFinal(); // includes Object.class
                default:
                    // case JAVA_LANG_OBJECT:
                    return t.getRawClass() == Object.class;
            }
        }
    };
    mapTyper.init(JsonTypeInfo.Id.CLASS, null);
    mapTyper.inclusion(JsonTypeInfo.As.PROPERTY);
    this.setDefaultTyping(mapTyper);
}};

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
        getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
        setterVisibility = JsonAutoDetect.Visibility.NONE,
        isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public static class ThrowableMixIn {

}
Kael
  • 1
  • 1
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 29 '23 at 06:51