October 27, 2024
Chicago 12, Melborne City, USA
java

SpringBoot OpenAPI definition polymorphism with anyOf and discriminatorProperty


How to define entity inheritance in a SpringBoot openapi type definition when POST-ing a @RequestBody containing an object map of polymorphic entities?

Shouldn’t it be sufficient to set anyOf = { PolarParrot.class, NorvegianParrot.class}, discriminatorProperty = "parrotType" in order to type hint which concrete Parrot instance must be used to resolve the RequestBody?

import java.lang.Override;
import java.net.URI;
import java.util.Map;

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;

@RestController
public class PolyParrot {
    @PostMapping(value = {"/create"}, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> create(@Valid @RequestBody Flock flock) {
        return ResponseEntity.created(URI.create("/flock/42")).build();
    }
}

@Schema(additionalProperties = Schema.AdditionalPropertiesValue.TRUE)
class Flock {
    @Schema
    Map<String, Parrot> birds;

    public Map<String, Parrot> getBirds() {
        return birds;
    }

    public void setBirds(Map<String, Parrot> birds) {
        this.birds = birds;
    }
}

@Schema(anyOf = {
        PolarParrot.class,
        NorvegianParrot.class,
}, discriminatorProperty = "parrotType")
interface Parrot {
    String getParrotType();
    String getPlummage();
}

@Schema()
class PolarParrot implements Parrot {
    @Schema(requiredMode = Schema.RequiredMode.REQUIRED, defaultValue = "polar", example = "polar")
    String parrotType;

    @Override
    public String getParrotType() {
        return parrotType;
    }

    @Override
    public String getPlummage() {
        return "red";
    }
}

@Schema()
class NorvegianParrot implements Parrot {
    @Schema(requiredMode = Schema.RequiredMode.REQUIRED, defaultValue = "norvegian", example = "norvegian")
    String parrotType;

    @Override
    public String getParrotType() {
        return parrotType;
    }

    @Override
    public String getPlummage() {
        return "blue";
    }
}

According to docs and examples it appears that anyOf + discriminatorProperty should be enough, yet, when POST-ing a valid payload instead of a deserialized flock object it throws:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of `Parrot` 
(no Creators, like default constructor, exist): 
abstract types either need to be mapped to concrete types, 
have custom deserializer, or contain additional type information

Doesn’t Parrot contain enough ‘additional type information’ already?

Which essential part did I miss here?



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video