-
-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Description
Bug Report Checklist
-
Have you provided a full/minimal spec to reproduce the issue?
-
Have you validated the input using an OpenAPI validator (example)?
I assume OpenAPI Generator's own samples are valid. -
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 ([Rust][Bounty] Issues with client code generation. #6178)
Description
Generated models contain incorrect implementations of add and remove helper methods for JsonNullable<List> fields.
The generated code fails to compile because these methods treat the field as an ArrayList, while its actual type is JsonNullable<List>.
openapi-generator version
7.20.0
OpenAPI declaration file content or url
paths:
/hello-world:
get:
tags:
- hello-world
operationId: hello-world
responses:
200:
description: Hello world
content:
application/json:
schema:
$ref: '#/components/schemas/BugResponse'
components:
schemas:
BugResponse:
type: object
properties:
nullableField:
type: string
nullable: true
x-is-jackson-optional-nullable: true
nullableList:
type: array
items:
type: string
nullable: true
x-is-jackson-optional-nullable: true
Command line used for generation
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.20.0</version>
<executions>
<execution>
<id>bug-test</id>
<goals>
<goal>generate</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/api/test.yml</inputSpec>
<generatorName>jaxrs-spec</generatorName>
<generateSupportingFiles>false</generateSupportingFiles>
<configOptions>
<interfaceOnly>true</interfaceOnly>
<useJakartaEe>true</useJakartaEe>
<serializableModel>true</serializableModel>
<useTags>true</useTags>
<openapiNullable>true</openapiNullable>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Steps to reproduce
- Copy the OpenAPI specification file to
src/main/resources/api/test.yml. - Run:
mvn generate-resources - Open the generated file:
target/generated-sources/openapi/src/gen/java/org/openapitools/model/BugResponse.java - Observe that the generated
add/removemethods reference the field asArrayList, while the actual type isJsonNullable<List<String>>, causing compilation errors.
Actual output
public BugResponse addNullableListItem(String nullableListItem) {
if (this.nullableList == null) {
this.nullableList = new ArrayList<>();
}
this.nullableList.add(nullableListItem);
return this;
}
public BugResponse removeNullableListItem(String nullableListItem) {
if (nullableListItem != null && this.nullableList != null) {
this.nullableList.remove(nullableListItem);
}
return this;
}
Expected output
public BugResponse addNullableListItem(String nullableListItem) {
if (this.nullableList == null || !this.nullableList.isPresent()) {
this.nullableList = JsonNullable.<List<String>>of(new ArrayList<>());
}
try {
this.nullableList.get().add(nullableListItem);
} catch (java.util.NoSuchElementException e) {
// this can never happen, as we make sure above that the value is present
}
return this;
}
public BugResponse removeNullableListItem(String nullableListItem) {
if (nullableListItem != null && this.nullableList != null && this.nullableList.isPresent()) {
try {
this.nullableList.get().remove(nullableListItem);
} catch (java.util.NoSuchElementException e) {
// this can never happen, as we make sure above that the value is present
}
}
return this;
}
Suggest a fix
Replace the generated implementation with the following:
openapi-generator/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pojo.mustache
Lines 174 to 191 in bd7fc7f
| {{#isArray}} | |
| public {{classname}} add{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { | |
| if (this.{{name}} == null) { | |
| this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}}; | |
| } | |
| this.{{name}}.add({{name}}Item); | |
| return this; | |
| } | |
| public {{classname}} remove{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { | |
| if ({{name}}Item != null && this.{{name}} != null) { | |
| this.{{name}}.remove({{name}}Item); | |
| } | |
| return this; | |
| } | |
| {{/isArray}} |
{{#isArray}}
public {{classname}} add{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) {
{{#vendorExtensions.x-is-jackson-optional-nullable}}
if (this.{{name}} == null || !this.{{name}}.isPresent()) {
this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}});
}
try {
this.{{name}}.get().add({{name}}Item);
} catch (java.util.NoSuchElementException e) {
// this can never happen, as we make sure above that the value is present
}
return this;
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{^vendorExtensions.x-is-jackson-optional-nullable}}
if (this.{{name}} == null) {
this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}};
}
this.{{name}}.add({{name}}Item);
return this;
{{/vendorExtensions.x-is-jackson-optional-nullable}}
}
public {{classname}} remove{{nameInPascalCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) {
{{#vendorExtensions.x-is-jackson-optional-nullable}}
if ({{name}}Item != null && this.{{name}} != null && this.{{name}}.isPresent()) {
try {
this.{{name}}.get().remove({{name}}Item);
} catch (java.util.NoSuchElementException e) {
// this can never happen, as we make sure above that the value is present
}
}
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{^vendorExtensions.x-is-jackson-optional-nullable}}
if ({{name}}Item != null && this.{{name}} != null) {
this.{{name}}.remove({{name}}Item);
}
{{/vendorExtensions.x-is-jackson-optional-nullable}}
return this;
}
{{/isArray}}
and
openapi-generator/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/pojo.mustache
Lines 192 to 209 in bd7fc7f
| {{#isMap}} | |
| public {{classname}} put{{nameInPascalCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { | |
| if (this.{{name}} == null) { | |
| this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}}; | |
| } | |
| this.{{name}}.put(key, {{name}}Item); | |
| return this; | |
| } | |
| public {{classname}} remove{{nameInPascalCase}}Item(String key) { | |
| if (this.{{name}} != null) { | |
| this.{{name}}.remove(key); | |
| } | |
| return this; | |
| } | |
| {{/isMap}} |
{{#isMap}}
public {{classname}} put{{nameInPascalCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) {
{{#vendorExtensions.x-is-jackson-optional-nullable}}
if (this.{{name}} == null || !this.{{name}}.isPresent()) {
this.{{name}} = JsonNullable.<{{{datatypeWithEnum}}}>of({{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}});
}
try {
this.{{name}}.get().put(key, {{name}}Item);
} catch (java.util.NoSuchElementException e) {
// this can never happen, as we make sure above that the value is present
}
return this;
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{^vendorExtensions.x-is-jackson-optional-nullable}}
if (this.{{name}} == null) {
this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}};
}
this.{{name}}.put(key, {{name}}Item);
return this;
{{/vendorExtensions.x-is-jackson-optional-nullable}}
}
public {{classname}} remove{{nameInPascalCase}}Item(String key) {
{{#vendorExtensions.x-is-jackson-optional-nullable}}
if (this.{{name}} != null && this.{{name}}.isPresent()) {
try {
this.{{name}}.get().remove(key);
} catch (java.util.NoSuchElementException e) {
// this can never happen, as we make sure above that the value is present
}
}
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{^vendorExtensions.x-is-jackson-optional-nullable}}
if (this.{{name}} != null) {
this.{{name}}.remove(key);
}
{{/vendorExtensions.x-is-jackson-optional-nullable}}
return this;
}
{{/isMap}}