Search code examples
javarestspring-bootjacksonjson-deserialization

Deserializing abstract property from JSON request body, based on another property from that body


How to properly deserialize an object from JSON (using Jackson) when it has an abstract property of which the conrete implementation depends on another property from that request body?

This is probably best described on an example.

Request body for my REST endpoint includes a property which is an abstract type with multiple concrete implementations which should be deserialized based on the discriminator property:

@Getter
@Builder
@AllArgsConstructor
public class SomeRequestBody {

    @NotNull
    @NotBlank
    @NumberFormat
    private final String discriminator;

    @NotNull
    private final SomeAbstractClass someAbstractClass;

}

Abstract class in question

@Getter
@AllArgsConstructor
public abstract class SomeAbstractClass{

    @NotNull
    @NotBlank
    protected final String commonProperty;

}

Example implementation of the abstract class

@Getter
public class ConcreteImplementationA extends SomeAbstractClass {

    @Builder
    @JsonCreator
    public ConcreteImplementationA(
        @NotNull @NotBlank @JsonProperty("commonProperty") String commonProperty,
        @NotNull @NotBlank @JsonProperty("specificProperty") Date specificProperty) {
            super(commonProperty);
            this.specificProperty = specificProperty;
    }

    @NotNull
    @NotBlank
    private final String specificProperty;

}

What I tried...

@Getter
@Builder
@AllArgsConstructor
public class SomeRequestBody {

    @NotNull
    @NotBlank
    @NumberFormat
    private final String discriminator;

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "discriminator")
    @JsonSubTypes({
        @JsonSubTypes.Type(value = ConcreteImplementationA.class, name = "1"),
        Other implementations...)
    })
    @NotNull
    private final SomeAbstractClass someAbstractClass;

}

However, I'm getting the following exception:

"Type definition error: [simple type, class abc.SomeRequestBody]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `abc.SomeRequestBody`, problem: Internal error: no creator index for property 'someAbstractClass' (of type com.fasterxml.jackson.databind.deser.impl.FieldProperty)\n at [Source: (PushbackInputStream); line: 8, column: 1]"

What is the proper way to achieve what I need?


Solution

  • Turns out it was not a problem with deserialization of that abstract property, but immutable properties and Lombok's @AllArgsConstructor not working well with Jackson. I have created a @JsonCreator annotated constructor instead of using the annotation and now it works fine.