I'm using OpenAPI 3.0 inheritance in components schemas and I have the (Java) classes generated by openapi-generator (which uses Jackson).
Why the discriminator property gets serialized twice in the resulting JSON?
This is a JHipster API-First project, which should use openapi-generator for generating the Java model (POJOs with Jackson annotations) and API controllers (interfaces with Spring's @Api annotations).
By following the OpenAPI 3.x documentation/examples, it seems that the property used as discriminator
must also be specified in the properties
list of the schema.
This way, the generated Java class seems to differ from the Jackson guidelines for polymorphic type handling with annotations (here), where the property used as discriminator must not be present in the class. Instead, the generated code also includes this property as a class attribute with getter/setter. This causes the JSON output to include the property twice, as shown below.
I've also tried to remove the property from the OpenAPI properties
list, leaving intact the discriminator
part; this way the generated code corresponds to the Jackson's guidelines and the serialization works just fine. On the other hand, I get an error during the deserialization process because the (removed) property is not found in the target class.
Following the OpenAPI 3.x doc guidelines:
TicketEvent:
type: object
description: A generic event
discriminator:
propertyName: type
required:
- id
- sequenceNumber
- timestamp
- type
properties:
id:
type: integer
format: int64
readOnly: true
sequenceNumber:
type: integer
readOnly: true
timestamp:
type: string
format: date-time
readOnly: true
type:
type: string
readOnly: true
TicketMovedEvent:
description: A ticket move event
allOf:
- $ref: '#/components/schemas/Event'
- type: object
required:
- source
- target
properties:
source:
$ref: '#/components/schemas/Queue'
target:
$ref: '#/components/schemas/Queue'
Generated class:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = TicketMovedEvent.class, name = "TicketMovedEvent")
})
public class TicketEvent {
...
@JsonProperty("type")
private String type;
The JSON includes the property twice:
{
...
"type": "TicketMovedEvent",
"type": null,
...
}
Removing the discriminator property from properties
list:
TicketEvent:
type: object
description: A generic event
discriminator:
propertyName: type
required:
- id
- sequenceNumber
- timestamp
properties:
id:
type: integer
format: int64
readOnly: true
sequenceNumber:
type: integer
readOnly: true
timestamp:
type: string
format: date-time
readOnly: true
Generated class without type
property:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = TicketMovedEvent.class, name = "TicketMovedEvent")
})
public class TicketEvent {
...
// now the "type" property is missing
})
The JSON now is correct:
{
...
"type": "TicketMovedEvent",
...
}
I would expect that, by following the OpenAPI 3.x guidelines, the generated class to be properly serialized/deserialized.
(sidenote)
During deserialization, by using the aforementioned approach, you might get the following error:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "type" (class it.blutec.bludesk.web.api.model.TicketMovedEvent), not marked as ignorable ...
To fix this, you need to configured the Jackson ObjectMapper object to ignore this kind of situations.
ObjectMapper om = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)