2

This question is similar to include class name in all objects serialized by jackson but I want to configure this by default without using annotations on each class. Preferably I'd like to add a field to the serialized json, such as _class or _type that contains the class name of the serialized class (preferably not event fully qualified, but I'm ok with that). I'm using Jackson 2.11.1.

For example let's say I have this interface:

public interface MyInterface {}

and two implementations:

public class MyClass1 implements MyInterface {

    private String something;

    public String getSomething() {
        return something;
    }

    public void setSomething(String something) {
        this.something = something;
    }
}

and

public class MyClass2 implements MyInterface {
    
    private String somethingElse;

    public String getSomethingElse() {
        return somethingElse;
    }

    public void setSomethingElse(String somethingElse) {
        this.somethingElse = somethingElse;
    }
}

I'd like to be able to serialize instances of MyClass1 and MyClass2 so that I can deserialize them as MyInterface (i.e. adding type information when serializing).

I've read in the documentation that you should be able to configure the ObjectMapper like this to make it work:

PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().build();
objectMapper.activateDefaultTyping(ptv); // default to using DefaultTyping.OBJECT_AND_NON_CONCRETE

But when I try this:

ObjectMapper objectMapper = new ObjectMapper();
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().build();
objectMapper.activateDefaultTyping(ptv); // default to using DefaultTyping.OBJECT_AND_NON_CONCRETE

MyClass1 myClass1 = new MyClass1();
myClass1.setSomething("something");

String json = objectMapper.writeValueAsString(myClass1);

MyInterface myInterface = objectMapper.readValue(json, MyInterface.class);

I get this exception:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class org.something.MyInterface
 at [Source: (String)"{"something":"something"}"; line: 1, column: 1]

If I change the configuration to use ObjectMapper.DefaultTyping.EVERYTHING then Jackson actually seem to wrap the type in an array but I still get an exception when trying to deserialize:

ObjectMapper objectMapper = new ObjectMapper();
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().build();
objectMapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING);

MyClass1 myClass1 = new MyClass1();
myClass1.setSomething("something");

String json = objectMapper.writeValueAsString(myClass1);

MyInterface myInterface = objectMapper.readValue(json, MyInterface.class);

the exception now is:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'org.something.MyClass1' as a subtype of `org.something.MyInterface`: Configured `PolymorphicTypeValidator` (of type `com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator`) denied resolution
 at [Source: (String)"["org.something.MyClass1",{"something":"something"}]"; line: 1, column: 53]

How can I resolve this?

Johan
  • 37,479
  • 32
  • 149
  • 237

2 Answers2

2

I actually got it working by configuring the ObjectMapper like this:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.activateDefaultTyping(new LaissezFaireSubTypeValidator(), ObjectMapper.DefaultTyping.EVERYTHING);

Not sure if this is the idiomatic way to do it though.

Johan
  • 37,479
  • 32
  • 149
  • 237
0

While building PolymorphicTypeValidator, if you provide the package where your sub classes reside, it should work fine. You need to do like this:

  ObjectMapper mapper = new ObjectMapper();
  PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
                .allowIfSubType("com.example.core")
                .build();
  mapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.EVERYTHING);

com.example.core

is the package where all of the subclasses reside.

roshan1892
  • 81
  • 1
  • 7