swagger-api / swagger-api/swagger-codegen

[Java] Incorrect enum generation for boolean-based data types

Open
#11,807 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Mustache
Stars
17.8k
Forks
6k
PR merge metrics
No merged PRs in 30d

Description

Description

Java code generator uses the wring jsonReader method in its read method when generating boolean-based enumerations.

Swagger-codegen version

3.0.33 and newer

Swagger declaration file content or url
Boolean1:
      description: True or False indicator
      type: boolean
      enum:
      - true
      - false

The resulting Java code looks as follows:

package some.package;

import java.util.Objects;
import java.util.Arrays;
import io.swagger.v3.oas.annotations.media.Schema;
import com.google.gson.annotations.SerializedName;
import java.io.IOException;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

/**
 * True or False indicator
 */
@JsonAdapter(Boolean1.Adapter.class)
public enum Boolean1 {
  TRUE(true),
  FALSE(false);

  private Boolean value;

  Boolean1(Boolean value) {
    this.value = value;
  }

  public Boolean getValue() {
    return value;
  }

  @Override
  public String toString() {
    return String.valueOf(value);
  }

  public static Boolean1 fromValue(Boolean input) {
    for (Boolean1 b : Boolean1.values()) {
      if (b.value.equals(input)) {
        return b;
      }
    }
    return null;
  }

  public static class Adapter extends TypeAdapter<Boolean1> {
    @Override
    public void write(final JsonWriter jsonWriter, final Boolean1 enumeration) throws IOException {
      jsonWriter.value(String.valueOf(enumeration.getValue()));
    }

    @Override
    public Boolean1 read(final JsonReader jsonReader) throws IOException {
      Object value = jsonReader.nextString();
      return Boolean1.fromValue((Boolean)(value));
    }
  }
}

The malicious part is in the read method. The jsonReader tries to consume a string as next token, but the JSON contains boolean value in this case.

    @Override
    public Boolean1 read(final JsonReader jsonReader) throws IOException {
      Object value = jsonReader.nextString();
      return Boolean1.fromValue((Boolean)(value));
    }
{"dateTime":"2022-06-02T12:02:00.888000","endOfList":false}

As a consequence, any boolean value gets rejected with the following error message:

java.lang.IllegalStateException: Expected a string but was BOOLEAN at line 1 column 59 path $.endOfList

Correct consumer method for boolean-based values should be:

    @Override
    public Boolean1 read(final JsonReader jsonReader) throws IOException {
      Boolean value = jsonReader.nextBoolean();
      return Boolean1.fromValue((Boolean)(value));
    }
  }
}
Command line used for generation

Download Java-Code from SwaggerHub, under Export > Client SDK > Java, or using Swagger Code Generator (e.g., in the command-line or as Maven plugin).

Steps to reproduce
  1. Create yaml specification with enum.
  2. Generate Java Code, or download Java Code from SwaggerHub
Related issues/PRs
Suggest a fix/enhancement

After inspecting the mustache template used to generate the enumeration, the definition seems not to consider boolean-based values (cf. /src/main/resources/Java/modelEnum.mustache).

    @Override
    public {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}} read(final JsonReader jsonReader) throws IOException {
      {{#isNumber}}BigDecimal value = new BigDecimal(jsonReader.nextDouble()){{/isNumber}}{{^isNumber}}{{#isInteger}}Integer value {{/isInteger}}{{^isInteger}}String value {{/isInteger}}= jsonReader.{{#isInteger}}nextInt(){{/isInteger}}{{^isInteger}}nextString(){{/isInteger}}{{/isNumber}};
      return {{#datatypeWithEnum}}{{{.}}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{{classname}}}{{/datatypeWithEnum}}.fromValue({{#jackson}}value{{/jackson}}{{^jackson}}String.valueOf(value){{/jackson}});
    }

The suggested solution is to add a case for boolean values.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/main/resources/Java/modelEnum.mustache and reproduce the issue using the boolean enum specification shown in the report. Check the generated enum's Adapter.read method and verify generation and consumption of boolean values; done means the generated code accepts the boolean JSON token without the reported exception.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, openapi
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.