Swagger-codegen: Is polymorphism supported in code generation?

Created on 29 Sep 2016  路  7Comments  路  Source: swagger-api/swagger-codegen

I've defined a POST method using polymorphism along the lines of the spec example at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#models-with-polymorphism-support . My real example is more complex but here's a simple alternative based on the spec example:

swagger: '2.0'
info:
  version: 1.0.0
  title: Pet Store Test
  description: Service for adding pets to a pet store.
basePath: /petstore
schemes:
  - http
paths:
  /pets:
    post:
      summary: Add a pet.
      description: Adds a new pet.
      consumes:
        - application/json
      parameters:
        - name: pet
          in: body
          description: The pet you want to add
          required: true
          schema:
            $ref: "#/definitions/Pet"
      responses:
        201:
          description: Pet added successfully
          headers:
            location:
              description: URL of the build request.
              type: string

definitions:
  Pet:
    type: object
    discriminator: petType
    properties:
      name:
        type: string
      petType:
        type: string
    required:
    - name
    - petType
  Cat:
    description: A representation of a cat
    allOf:
    - $ref: '#/definitions/Pet'
    - type: object
      properties:
        huntingSkill:
          type: string
          description: The measured skill for hunting
          default: lazy
          enum:
          - clueless
          - lazy
          - adventurous
          - aggressive
      required:
      - huntingSkill
  Dog:
    description: A representation of a dog
    allOf:
    - $ref: '#/definitions/Pet'
    - type: object
      properties:
        packSize:
          type: integer
          format: int32
          description: the size of the pack the dog is from
          default: 0
          minimum: 0
      required:
      - packSize

I can generate the Spring server-side stubs for this yaml using the Swagger Editor. But when I POST json requests of a Cat or a Dog only the base part is detected by the server-side code - i.e., name and petType in this case. All the derived data is lost. I was hoping to cast the retrieved Pet to a Cat or Dog based on the value of petType but that fails at runtime with a ClassCastException.

Actually I confess I haven't tried testing this with the above yaml but what I say is true for my yaml. I just wanted to use the above as a simple analogy to ask the question of whether polymorphism is currently supported by swagger-codegen? If not then I'm going to have to completely rewrite my real yaml...

Composition / Inheritance Question

All 7 comments

More details: I put the swagger.yaml above in Swagger Editor 2.10.3 and did Generate Server > Spring. Then I changed the generated io.swagger.api.PetsApiController.java class to this:

@Controller
public class PetsApiController implements PetsApi {

    public ResponseEntity<Void> petsPost(@ApiParam(value = "The pet you want to add" ,required=true ) @RequestBody Pet pet) {
        System.out.println("Pet =\n" + pet);
        String petType = pet.getPetType();
        if (petType.equals("Cat")) {
            Cat cat = (Cat) pet;
            System.out.println("Hunting skill: " + cat.getHuntingSkill());
        }
        if (petType.equals("Dog")) {
            Dog dog = (Dog) pet;
            System.out.println("Pack size: " + dog.getPackSize());
        }   
        return new ResponseEntity<Void>(HttpStatus.OK);
    }
}

Then fire POST requests using either of these as the message body:

{
  "name": "Kitty",
  "petType": "Cat",
  "huntingSkill": "lazy"
}

or

{
  "name": "Fido",
  "petType": "Dog",
  "packSize": 10
}

Output for Cat request:

Pet =
class Pet {
    name: Kitty
    petType: Cat
}
2016-09-30 10:16:04.967 ERROR 122632 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with pa
th [/petstore] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: io.swagger.model.Pet cannot be cast to io.swagger.model.Cat]
with root cause
java.lang.ClassCastException: io.swagger.model.Pet cannot be cast to io.swagger.model.Cat
        at io.swagger.api.PetsApiController.petsPost(PetsApiController.java:35) ~[classes/:na]

and for Dog request:

Pet =
class Pet {
    name: Fido
    petType: Dog
}
2016-09-30 10:15:51.281 ERROR 122632 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with pa
th [/petstore] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: io.swagger.model.Pet cannot be cast to io.swagger.model.Dog]
with root cause
java.lang.ClassCastException: io.swagger.model.Pet cannot be cast to io.swagger.model.Dog
        at io.swagger.api.PetsApiController.petsPost(PetsApiController.java:39) ~[classes/:na]

So, am I doing something wrong or is this just not supported? I'd appreciate an answer today if possible otherwise I'm going to have to rewrite my spec, providing duplicate methods for each child type; one to post a Cat, another to post a Dog, etc. I'd rather avoid doing that if possible.

I'm surprised no-one's responded to this yet:(. BTW I'm using a Java Spring server and a Python client in case support for polymorphism varies across the various client/server and language/framework combinations.

See #1253 #1686 #2449 discriminator polymorphism is currently not supported in java based clients/servers.

@cbornet That's a bombshell for me! Do Python clients support polymorphism though?
[Updated 16/1/17: No! Python clients do not support polymorphism! See #1869. Aarghh!]

@cbornet By looking at the other issues you referenced (thanks!) I was able to hack the generated code by importing various com.fasterxml.jackson.annotation annotations. Specifically I added the following annotations to the autogenerated base class Pet:

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

Weirdly, in my PetsApiController class pet.getPetType() was returning null. I don't understand why. Is this expected behaviour? I would've expected it to be "Cat" or "Dog". In any case I had to change the code to this in PetsApiController:

    public ResponseEntity<Void> petsPost(@ApiParam(value = "The pet you want to add" ,required=true ) @RequestBody Pet pet) {
        System.out.println("Pet =\n" + pet);
        if (pet instanceof Cat) {
            Cat cat = (Cat) pet;
            System.out.println("Hunting skill: " + cat.getHuntingSkill());
        }
        if (pet instanceof Dog) {
            Dog dog = (Dog) pet;
            System.out.println("Pack size: " + dog.getPackSize());
        }

        // Note petType is null!
        System.out.println("Pet type: " + pet.getPetType());

        return new ResponseEntity<Void>(HttpStatus.OK);
    }

I also had to uppercase the value in the Cat request:

{
  "name": "Kitty",
  "petType": "Cat",
  "huntingSkill": "LAZY"
}

But then it worked! Sample output from a Cat and then a Dog request:

Pet =
class Cat {
    class Pet {
        name: Kitty
        petType: null
    }
    huntingSkill: lazy
}
Hunting skill: lazy
Pet type: null
Pet =
class Dog {
    class Pet {
        name: Fido
        petType: null
    }
    packSize: 10
}
Pack size: 10
Pet type: null

What is the status if this issue? Which languages support polymorphism? For which languages polymorphism status is in progress?

Was this page helpful?
0 / 5 - 0 ratings