Skip to content

Commit e660eb3

Browse files
[Java][native] Fix oneOf/anyOf with array or map members generating invalid code (#25012)
* [Java][native] Fix oneOf/anyOf models with array or map members Generic container members of oneOf/anyOf generated invalid Java in the native client, e.g. `getList<String>()` and `List<String>.class`. Iterate over composedSchemas like the jersey2/jersey3/okhttp-gson templates: use the erased type for class literals and instance checks, a TypeReference to deserialize containers, sanitized getter names, and skip constructors/instance checks whose erasure is duplicated. Fixes #22304 Fixes #22249 * Update samples * [Java][native] Gate anyOf array members on a JSON array and format map keys with %s * Update samples * Update samples for the jackson version bump on master * Update samples for the 7.27.0-SNAPSHOT version bump
1 parent 83a4a0c commit e660eb3

94 files changed

Lines changed: 7558 additions & 645 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/samples-java-client-jdk11.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ on:
1818
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
1919
- samples/client/others/java/jersey2-oneOf-duplicates/**
2020
- samples/client/others/java/jersey2-oneOf-Mixed/**
21+
- samples/client/others/java/native-oneOf-array/**
2122
- samples/client/others/java/okhttp-gson-streaming/**
2223
- samples/client/others/java/resteasy/**
2324
- samples/client/others/java/apache-httpclient/**
@@ -40,6 +41,7 @@ on:
4041
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
4142
- samples/client/others/java/jersey2-oneOf-duplicates/**
4243
- samples/client/others/java/jersey2-oneOf-Mixed/**
44+
- samples/client/others/java/native-oneOf-array/**
4345
- samples/client/others/java/okhttp-gson-streaming/**
4446
- samples/client/others/java/resteasy/**
4547
- samples/client/others/java/apache-httpclient/**
@@ -109,6 +111,7 @@ jobs:
109111
- samples/client/others/java/webclient-useAbstractionForFiles/
110112
- samples/client/others/java/jersey2-oneOf-duplicates/
111113
- samples/client/others/java/jersey2-oneOf-Mixed/
114+
- samples/client/others/java/native-oneOf-array/
112115
- samples/client/others/java/resttemplate-list-schema-validation/
113116
- samples/client/petstore/java/okhttp-gson-3.1-duplicated-operationid/
114117
- samples/client/others/java/okhttp-gson-streaming/
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
generatorName: java
2+
outputDir: samples/client/others/java/native-oneOf-array
3+
library: native
4+
inputSpec: modules/openapi-generator/src/test/resources/3_0/java/native/issue22304.yaml
5+
templateDir: modules/openapi-generator/src/main/resources/Java
6+
additionalProperties:
7+
hideGenerationTimestamp: "true"

‎modules/openapi-generator/src/main/resources/Java/libraries/native/anyof_model.mustache‎

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,31 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
8787
return ret;
8888
}
8989
{{/discriminator}}
90+
{{#composedSchemas}}
9091
{{#anyOf}}
91-
// deserialize {{{.}}}
92+
// deserialize {{{dataType}}}
9293
try {
94+
{{#isArray}}
95+
if (!tree.isArray()) {
96+
throw new IllegalArgumentException("not a JSON array");
97+
}
98+
{{/isArray}}
99+
{{#isContainer}}
93100
{{^useJackson3}}
94-
deserialized = tree.traverse(jp.getCodec()).readValueAs({{{.}}}.class);
101+
deserialized = tree.traverse(jp.getCodec()).readValueAs(new {{jacksonPackage}}.core.type.TypeReference<{{{dataType}}}>() {});
95102
{{/useJackson3}}
96103
{{#useJackson3}}
97-
deserialized = ctxt.readTreeAsValue(tree, {{{.}}}.class);
104+
deserialized = ctxt.readTreeAsValue(tree, ctxt.getTypeFactory().constructType(new {{jacksonPackage}}.core.type.TypeReference<{{{dataType}}}>() {}));
98105
{{/useJackson3}}
106+
{{/isContainer}}
107+
{{^isContainer}}
108+
{{^useJackson3}}
109+
deserialized = tree.traverse(jp.getCodec()).readValueAs({{{dataType}}}.class);
110+
{{/useJackson3}}
111+
{{#useJackson3}}
112+
deserialized = ctxt.readTreeAsValue(tree, {{{dataType}}}.class);
113+
{{/useJackson3}}
114+
{{/isContainer}}
99115
{{classname}} ret = new {{classname}}();
100116
ret.setActualInstance(deserialized);
101117
return ret;
@@ -105,6 +121,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
105121
}
106122

107123
{{/anyOf}}
124+
{{/composedSchemas}}
108125
{{^useJackson3}}
109126
throw new IOException("Failed deserialization for {{classname}}: no match found");
110127
{{/useJackson3}}
@@ -159,17 +176,23 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
159176
return Objects.hash(getActualInstance(), isNullable(), getSchemaType(), additionalProperties);
160177
}
161178
{{/additionalPropertiesType}}
179+
{{#composedSchemas}}
162180
{{#anyOf}}
163-
public {{classname}}({{{.}}} o) {
164-
super("anyOf", {{#isNullable}}Boolean.TRUE{{/isNullable}}{{^isNullable}}Boolean.FALSE{{/isNullable}});
181+
{{^vendorExtensions.x-duplicated-data-type}}
182+
public {{classname}}({{{dataType}}} o) {
183+
this();
165184
setActualInstance(o);
166185
}
167186

187+
{{/vendorExtensions.x-duplicated-data-type}}
168188
{{/anyOf}}
189+
{{/composedSchemas}}
169190
static {
191+
{{#composedSchemas}}
170192
{{#anyOf}}
171-
schemas.put("{{{.}}}", {{{.}}}.class);
193+
schemas.put("{{{dataType}}}", {{#isContainer}}{{{baseType}}}{{/isContainer}}{{^isContainer}}{{{dataType}}}{{/isContainer}}.class);
172194
{{/anyOf}}
195+
{{/composedSchemas}}
173196
JSON.registerDescendants({{classname}}.class, Collections.unmodifiableMap(schemas));
174197
{{#discriminator}}
175198
// Initialize and register the discriminator mappings.
@@ -190,7 +213,7 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
190213
/**
191214
* Set the instance that matches the anyOf child schema, check
192215
* the instance parameter is valid against the anyOf child schemas:
193-
* {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}
216+
* {{#anyOf}}{{.}}{{^-last}}, {{/-last}}{{/anyOf}}
194217
*
195218
* It could be an instance of the 'anyOf' schemas.
196219
* The anyOf child schemas may themselves be a composed schema (allOf, anyOf, anyOf).
@@ -204,40 +227,48 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im
204227
}
205228

206229
{{/isNullable}}
230+
{{#composedSchemas}}
207231
{{#anyOf}}
208-
if (JSON.isInstanceOf({{{.}}}.class, instance, new HashSet<Class<?>>())) {
232+
{{^vendorExtensions.x-duplicated-data-type}}
233+
if (JSON.isInstanceOf({{#isContainer}}{{{baseType}}}{{/isContainer}}{{^isContainer}}{{{dataType}}}{{/isContainer}}.class, instance, new HashSet<Class<?>>())) {
209234
super.setActualInstance(instance);
210235
return;
211236
}
212237

238+
{{/vendorExtensions.x-duplicated-data-type}}
213239
{{/anyOf}}
240+
{{/composedSchemas}}
214241
throw new RuntimeException("Invalid instance type. Must be {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}");
215242
}
216243

217244
/**
218245
* Get the actual instance, which can be the following:
219-
* {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}
246+
* {{#anyOf}}{{.}}{{^-last}}, {{/-last}}{{/anyOf}}
220247
*
221-
* @return The actual instance ({{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}})
248+
* @return The actual instance ({{#anyOf}}{{.}}{{^-last}}, {{/-last}}{{/anyOf}})
222249
*/
223250
@Override
224251
public Object getActualInstance() {
225252
return super.getActualInstance();
226253
}
227254

255+
{{#composedSchemas}}
228256
{{#anyOf}}
257+
{{^vendorExtensions.x-duplicated-data-type-ignoring-erasure}}
229258
/**
230-
* Get the actual instance of `{{{.}}}`. If the actual instance is not `{{{.}}}`,
259+
* Get the actual instance of `{{dataType}}`. If the actual instance is not `{{dataType}}`,
231260
* the ClassCastException will be thrown.
232261
*
233-
* @return The actual instance of `{{{.}}}`
234-
* @throws ClassCastException if the instance is not `{{{.}}}`
262+
* @return The actual instance of `{{dataType}}`
263+
* @throws ClassCastException if the instance is not `{{dataType}}`
235264
*/
236-
public {{{.}}} get{{{.}}}() throws ClassCastException {
237-
return ({{{.}}})super.getActualInstance();
265+
public {{{dataType}}} get{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}() throws ClassCastException {
266+
return ({{{dataType}}})super.getActualInstance();
238267
}
239268

269+
{{/vendorExtensions.x-duplicated-data-type-ignoring-erasure}}
240270
{{/anyOf}}
271+
{{/composedSchemas}}
241272

242273
{{#supportUrlQuery}}
243274

0 commit comments

Comments
 (0)