swagger-api/swagger-codegen-generators

[Java] [Jackson] discriminator is annotated with @JsonTypeId, which causes empty "type" on serialization

andreikubar opened this issue · 1 comments

We generate the oas3 schema from the java classes which use polymorphism. The schema that we get looks right:

{
    "openapi": "3.0.1",
    "info": {
        "title": "OpenAPI definition",
        "version": "v0"
    },
    "servers": [
        {
            "url": "http://localhost:8080",
            "description": "Generated server url"
        }
    ],
    "paths": {
        "/pets/{name}": {
            "get": {
                "tags": [
                    "Pets"
                ],
                "summary": "get a pet",
                "operationId": "getPet",
                "parameters": [
                    {
                        "name": "name",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "*/*": {
                                "schema": {
                                    "$ref": "#/components/schemas/PetResponse"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "Cat": {
                "type": "object",
                "allOf": [
                    {
                        "$ref": "#/components/schemas/Pet"
                    }
                ]
            },
            "Dog": {
                "type": "object",
                "allOf": [
                    {
                        "$ref": "#/components/schemas/Pet"
                    }
                ]
            },
            "Pet": {
                "required": [
                    "type"
                ],
                "type": "object",
                "properties": {
                    "name": {
                        "type": "string"
                    },
                    "type": {
                        "type": "string"
                    }
                },
                "discriminator": {
                    "propertyName": "type"
                }
            },
            "PetResponse": {
                "type": "object",
                "properties": {
                    "pet": {
                        "oneOf": [
                            {
                                "$ref": "#/components/schemas/Cat"
                            },
                            {
                                "$ref": "#/components/schemas/Dog"
                            }
                        ]
                    }
                }
            }
        }
    }
}

The relevant source Java classes look like this:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Cat.class, name = "Cat"),
        @JsonSubTypes.Type(value = Dog.class, name = "Dog")
})
public abstract class Pet {
    public String name;
}

public class Dog extends Pet{

}

public class Cat extends Pet{

}

What we get from the codegen (we use swagger-codegen-maven-plugin version 3.0.46) is this:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true )
@JsonSubTypes({
        @JsonSubTypes.Type(value = Cat.class, name = "Cat"),
        @JsonSubTypes.Type(value = Dog.class, name = "Dog"),
})


public class Pet   {
  @JsonProperty("name")
  private String name = null;

  @JsonTypeId
  private String type = null;

...

When we serialize:

Cat cat = (Cat) new Cat().name("charly");
objectMapper.writeValueAsString(cat);

result looks like this:

{
  "type" : "",
  "name" : "charly"
}

For the serialization to work properly, I have to modify the generated Pet.java as follows:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true )
@JsonSubTypes({
        @JsonSubTypes.Type(value = Cat.class, name = "Cat"),
        @JsonSubTypes.Type(value = Dog.class, name = "Dog"),
})
@JsonIgnoreProperties(
        value = "type", // ignore manually set type, it will be automatically generated by Jackson during serialization
        allowSetters = true // allows the type to be set during deserialization
)

public class Pet   {
  @JsonProperty("name")
  private String name = null;

  private String type = null;

...

So I remove the @JsonTypeId and I add the @JsonIgnoreProperties for the type variable.
Which I think as how the code should be generated. @JsonTypeId marks a field for overriding the type id. So since type is null, we get an empty value in JSON. What should be done is that the discriminator property should be ignored, so that Jackson can populate it with the type name automatically.

Looks like the @JsonTypeId was added as result of this issue: #105 Back then it looks like Jackson was behaving differently and type was still populated. This looks however like it was an incorrect handling of @JsonTypeId on Jackson side.