handle InvalidTypeIdException with jackson

Viewed 668

Here is Base class annotated with JsonType and JsonSubTypes with mandatory "type" field required.

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true)
@JsonSubTypes(value = {
    @JsonSubTypes.Type(value = SubA.class, name = "A"),
    @JsonSubTypes.Type(value = SubB.class, name = "B")
})
public abstract class Base {
    private String type;

    public String getType()
    {
        return type;
    }

    public void setType(String type)
    {
        this.type = type;
    }
}

There is below class which wraps above class and is used in controller. This wrapper class is taking "type" already which need to be passed to class Base above

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
class BaseWrapper {
    @NotBlank
    private String type;
    @NotNull
    @Valid
    private Base base;

}

Finally , validation are here in controller

@PostMapping("/createBase")
public ResponseEntity<ResponseDto> createBase(@RequestBody @Valid BaseWrapper)
{    
    ...
}

Sample JSON that works with controller validations in spring boot -

{
  "type":"A",
  "base": {
     "type:"A",
     //fields of A
  }
}

How can the duplicate type be removed from inside base itself like below. this gives InvalidIdException from Jackson.

{
  "type":"A",
  "base": {
     //fields of A
  }
}

I understand using Custom Deserializer is one way but then JsonTypeInfo, JsonSubTypeInfo and all validations being done by default does not apply and everything has to be hand-crafted.

Can the type required twice be fixed so that only outer Json type is propagated to inner and Jackson understands it ?

1 Answers

You can use JsonTypeInfo.As.EXTERNAL_PROPERTY but note from javadoc:

Inclusion mechanism similar to PROPERTY, except that property is included one-level higher in hierarchy, i.e. as sibling property at same level as JSON Object to type. Note that this choice can only be used for properties, not for types (classes). Trying to use it for classes will result in inclusion strategy of basic PROPERTY instead.

So this will not work on class level but on property in wrapper

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@Setter
public static class BaseWrapper {
    @NotBlank
    private String type;

    @NotNull
    @Valid
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type", visible = true)
    @JsonSubTypes(value = {
            @JsonSubTypes.Type(value = SubA.class, name = "A"),
            @JsonSubTypes.Type(value = SubB.class, name = "B")
    })
    private Base base;

}
Related