openapi-generator: [BUG] OpenAPI Generator fails to generate array models

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What’s the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

OpenAPI generator fails to generate array models. It complains with:

[main] INFO  o.o.codegen.DefaultGenerator - Model {} not generated since it's an alias to array (without property) and `generateAliasAsModel` is set to false (default)
openapi-generator version

v4.3.1

And also tried latest master using Docker 07c23f4d1ad184554968b1bbe858b4ad987dd698.

OpenAPI declaration file content or url
openapi: '3.0.3'

info:
  title: Test
  version: 0.1.0

paths: {}

components:
  schemas:
    Test:
      type: array
      items:
        type: object
        properties:
          foo:
            type: string
Generation Details
docker run --rm \                                
  -v ${PWD}:/local openapitools/openapi-generator-cli generate \    
  -i /local/openapi.yaml \ 
  -g go \
  -o /local/out/go
Steps to reproduce
  1. Generate a client for any language using the provided schema. Note that the array model is not generated with the log message from this bug’s description.
Related issues/PRs
Suggest a fix

The following code is erroneous. An array has items not properties (Even required by the OpenAPI spec). You might need to cast Schema to ArraySchema to get access to items or something like that. https://github.com/OpenAPITools/openapi-generator/blob/41851b45e1f1ffa7f0df36270a6b3c0cee6425bb/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java#L485-L488

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 11
  • Comments: 25 (4 by maintainers)

Most upvoted comments

Another dirty hack to get the generator to behave, at least on Node, is just to add Array’s built-in length attribute as a property. Like this:

Something:
  type: array
  properties:
    length:
      type: integer
  items:
    $ref: '#/components/schemas/SomethingElse'

Also happening with Python, FastAPI and Pydantic v2. the generator worked correctly with the previous version of Pydantic though. Any thoughts?

Hello!

I found a workaround so that the OpenAPI generator generates valid models when an array is needed.

I tried to wrap the array in a top-level object, like in this example input file test_wrapper.yaml. You can see that the wrapper object is the only difference from my original example and the OpenAPI generator successfully created a good model, without using the --generate-alias-as-model option.

This worked with openapi-generator-cli-5.1.0-20210108.143511-2.jar. I didn’t try it with other versions of the generator.

The command I used is:

java -jar openapi-generator-cli-5.1.0-20210108.143511-2.jar generate -g python-flask -i test_wrapper.yaml -o test_wrapper_schema/ --additional-properties packageName=test

…with test_wrapper.yaml as input file.

The files generated in the test_wrapper_schema/test/models folder are: test_schema.py and test_schema_array_wrapper.py, which work well.

You can see that test_schema.py contains a Python list of the generated object test_schema_array_wrapper.py, so it managed to create an object that contains the desired array:

def __init__(self, array_wrapper=None):  # noqa: E501
        """TestSchema - a model defined in OpenAPI
        :param array_wrapper: The array_wrapper of this TestSchema.  # noqa: E501
        :type array_wrapper: List[TestSchemaArrayWrapper]
        """
        self.openapi_types = {
            'array_wrapper': List[TestSchemaArrayWrapper]
        }

        self.attribute_map = {
            'array_wrapper': 'array-wrapper'
        }

        self._array_wrapper = array_wrapper

I don’t know if this workaround works for any use-case, but I hope it helps anyone who needs it and maybe aids in solving the bug.

Thanks!

Did you try setting the generateAliasAsModel option to true?

@keysight-diana-belmega Thanks for putting this MCVE/PoC together Diana!

A colleague and I were able to reproduce this with v3.3.4 and the latest v5.0.1 (deployed via the wget method, as per the installation instructions).

This use case seems to be right out of the Swagger examples (i.e. array of objects), so it seems odd that this error would arise with recent builds given it’s likely a common/typical use case in OpenAPI-generated APIs/specs.

Edit: we were able to get it going by using a recent build (i.e. v5.0.1) plus the --generate-alias-as-model flag to the tool. The question remains: does it make sense that one should have to explicitly set said flag/option for such a use case?

Edit: nope: using the --generate-alias-as-model flag just makes the compilation errors go away. The actual auto-generated code is empty (i.e. blank schema).

Any news?

Any news on this issue? Still applicable in 2023. I have the same result as Bsd15

EDIT: I found a “workaround”, which work well with springboot generator, you have to define the array as a schema, but the items aswell:

    TranslatedField:
      type: array
      items:
        $ref: "#/components/schemas/TranslatedFieldItem"
    TranslatedFieldItem:
      type: object
      required:
        - value
      properties:
        lang:
          $ref: "#/components/schemas/Language"
        value:
          type: string

Another dirty hack to get the generator to behave, at least on Node, is just to add Array’s built-in length attribute as a property. Like this:

Something:
  type: array
  properties:
    length:
      type: integer
  items:
    $ref: '#/components/schemas/SomethingElse'

Can confirm this also worked with Springboot

This generates a model with the length property only. and nothing else.

components:
  schemas:
    Cat:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
        name:
          type: string
        description:
          type: string
        status:
            type: string
            enum:
              - available
              - adopted
              - dead
    Cats:
      type: array
      properties:
        length:
          type: integer
      items:
        $ref: '#/components/schemas/Cat'

Cats.java

package org.openapitools.model;

import java.net.URI;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.Arrays;
import org.openapitools.jackson.nullable.JsonNullable;
import java.util.NoSuchElementException;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
import javax.validation.constraints.*;
import io.swagger.v3.oas.annotations.media.Schema;


import java.util.*;
import javax.annotation.Generated;

/**
 * Cats
 */

@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2022-12-06T13:01:52.635460+05:30[Asia/Kolkata]")
public class Cats {

  @JsonProperty("length")
  private JsonNullable<Object> length = JsonNullable.undefined();

  public Cats length(Object length) {
    this.length = JsonNullable.of(length);
    return this;
  }

  /**
   * Get length
   * @return length
  */
  
  @Schema(name = "length", required = false)
  public JsonNullable<Object> getLength() {
    return length;
  }

  public void setLength(JsonNullable<Object> length) {
    this.length = length;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Cats cats = (Cats) o;
    return equalsNullable(this.length, cats.length);
  }

  private static <T> boolean equalsNullable(JsonNullable<T> a, JsonNullable<T> b) {
    return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get()));
  }

  @Override
  public int hashCode() {
    return Objects.hash(hashCodeNullable(length));
  }

  private static <T> int hashCodeNullable(JsonNullable<T> a) {
    if (a == null) {
      return 1;
    }
    return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31;
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("class Cats {\n");
    sb.append("    length: ").append(toIndentedString(length)).append("\n");
    sb.append("}");
    return sb.toString();
  }

  /**
   * Convert the given object to string with each line indented by 4 spaces
   * (except the first line).
   */
  private String toIndentedString(Object o) {
    if (o == null) {
      return "null";
    }
    return o.toString().replace("\n", "\n    ");
  }
}

Am I missing anything else that I should add?

Another dirty hack to get the generator to behave, at least on Node, is just to add Array’s built-in length attribute as a property. Like this:

Something:
  type: array
  properties:
    length:
      type: integer
  items:
    $ref: '#/components/schemas/SomethingElse'

Can confirm this also worked with Springboot