Skip to content

[BUG][JAVA][microprofile] Required property typed [object, "null"] generates an uncompilable = ; initializer #24776

Description

@cotris-js

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • What's the version of OpenAPI Generator used?
  • Have you search for related issues/PRs?
  • What's the actual output vs expected output?

Description

With library=microprofile, a property that is required and nullable via an OpenAPI
3.1 type array over a container type generates a field with an empty initializer:

private Map<String, Object> nullableMap = ;
private List<String> nullableArray = ;

javac then fails with illegal start of expression. Generation itself reports success,
so the failure only surfaces at compile time.

The trigger is required + type array containing "null" + a container type. Each part
matters:

property generated ok
required, type: [object, "null"] + additionalProperties Map<String, Object> x = ;
required, type: [array, "null"] + items List<String> x = ;
not required, type: [object, "null"] Map<String, Object> x = null;
required, plain type: object Map<String, Object> x = new HashMap<>();

Cause

Java/libraries/microprofile/pojo.mustache interpolates defaultValue unguarded on the
required branch:

{{#isContainer}}
  private {{{datatypeWithEnum}}} {{name}}{{#required}} = {{{defaultValue}}}{{/required}}{{^required}} = null{{/required}};
{{/isContainer}}

AbstractJavaCodegen.toDefaultValue returns Java null for a nullable container, which
is correct:

} else if (ModelUtils.isMapSchema(schema) && !(ModelUtils.isComposedSchema(schema))) {
    ...
    // nullable or containerDefaultToNull set to true
    if (cp.isNullable || containerDefaultToNull) {
        return null;
    }

{{{defaultValue}}} therefore renders as the empty string and leaves the bare =. The
{{^required}} branch hardcodes = null and so never hits this.

The generic Java/pojo.mustache guards the same interpolation with {{#defaultValue}},
which is why this is microprofile-only — okhttp-gson and native generate the spec
below correctly, with no initializer.

openapi-generator version

Reproduces on 7.13.0 and on 7.25.0. Not a regression as far as I can tell — the
unguarded interpolation is present in both.

Not affected by openApiNullable or containerDefaultToNull (tried both true and false),
nor by adding default: {} / default: null to the property, nor by expressing
nullability as anyOf/oneOf with type: "null".

OpenAPI declaration file

openapi: 3.1.0
info: {title: repro, version: "1.0.0"}
paths:
  /thing:
    get:
      operationId: getThing
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema: {$ref: "#/components/schemas/Thing"}
components:
  schemas:
    Thing:
      type: object
      properties:
        nullableMap:
          type: [object, "null"]
          additionalProperties: true
        nullableArray:
          type: [array, "null"]
          items: {type: string}
      required: [nullableMap, nullableArray]

Command line used for generation

openapi-generator-cli generate -i spec.yaml -g java -o out \
  --library microprofile \
  --additional-properties microprofileRestClientVersion=3.0

Steps to reproduce

  1. Generate with the command above.
  2. grep 'nullableMap' out/src/main/java/org/openapitools/client/model/Thing.java
  3. Observe private Map<String, Object> nullableMap = ;
  4. javac on the generated sources fails with illegal start of expression.

Swapping --library microprofile for native or omitting it (okhttp-gson) produces
compilable output for the same spec.

Suggested fix

Give the required branch the same fallback the non-required branch already has:

{{#isContainer}}
  private {{{datatypeWithEnum}}} {{name}}{{#required}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^required}} = null{{/required}};
{{/isContainer}}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions