OpenAPITools / OpenAPITools/openapi-generator
[BUG][JAVA] Generated model with generics doesn't compile with escaped code
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Description
In my OpenAPI specification I have an object:
WithUnknownData:
type: object
title: WithUnknownData
x-internal: true
properties:
data:
oneOf:
- type: object
- type: array
items: {}
I configured the generator to generate a Java client:
{
"generatorName": "java",
"inputSpec": "docs/openapi.yaml",
"output": "generated-clients/java"
}
When I look at the generated model, I get:
public class WithUnknownData {
public static final String SERIALIZED_NAME_DATA = "data";
@SerializedName(SERIALIZED_NAME_DATA)
private WithUnknownDataData data;
public WithUnknownData() {
}
public WithUnknownData data(WithUnknownDataData data) {
this.data = data;
return this;
}
/* And more code*/
}
When I drill down to WithUnknownDataData I see it does not compile, because some of the generated code is escaped:
public static class CustomTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!WithUnknownDataData.class.isAssignableFrom(type.getRawType())) {
return null; // this class only serializes 'WithUnknownDataData' and its subtypes
}
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
final TypeAdapter<List<Object>> adapterList<Object> = gson.getDelegateAdapter(this, TypeToken.get(List<Object>.class));
final TypeAdapter<Object> adapterObject = gson.getDelegateAdapter(this, TypeToken.get(Object.class));
return (TypeAdapter<T>) new TypeAdapter<WithUnknownDataData>() {
@Override
public void write(JsonWriter out, WithUnknownDataData value) throws IOException {
if (value == null || value.getActualInstance() == null) {
elementAdapter.write(out, null);
return;
}
// check if the actual instance is of the type `List<Object>`
if (value.getActualInstance() instanceof List<Object>) {
JsonObject obj = adapterList<Object>.toJsonTree((List<Object>)value.getActualInstance()).getAsJsonObject();
elementAdapter.write(out, obj);
return;
}
// check if the actual instance is of the type `Object`
if (value.getActualInstance() instanceof Object) {
JsonObject obj = adapterObject.toJsonTree((Object)value.getActualInstance()).getAsJsonObject();
elementAdapter.write(out, obj);
return;
}
throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: List<Object>, Object");
}
@Override
public WithUnknownDataData read(JsonReader in) throws IOException {
Object deserialized = null;
JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject();
int match = 0;
ArrayList<String> errorMessages = new ArrayList<>();
TypeAdapter actualAdapter = elementAdapter;
// deserialize List<Object>
try {
// validate the JSON object to see if any exception is thrown
List<Object>.validateJsonObject(jsonObject);
actualAdapter = adapterList<Object>;
match++;
log.log(Level.FINER, "Input data matches schema 'List<Object>'");
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for List<Object> failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'List<Object>'", e);
}
// deserialize Object
try {
// validate the JSON object to see if any exception is thrown
Object.validateJsonObject(jsonObject);
actualAdapter = adapterObject;
match++;
log.log(Level.FINER, "Input data matches schema 'Object'");
} catch (Exception e) {
// deserialization failed, continue
errorMessages.add(String.format("Deserialization for Object failed with `%s`.", e.getMessage()));
log.log(Level.FINER, "Input data does not match schema 'Object'", e);
}
if (match == 1) {
WithUnknownDataData ret = new WithUnknownDataData();
ret.setActualInstance(actualAdapter.fromJsonTree(jsonObject));
return ret;
}
throw new IOException(String.format("Failed deserialization for WithUnknownDataData: %d classes match result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", match, errorMessages, jsonObject.toString()));
}
}.nullSafe();
}
}
As you can see some the code looks like this:
final TypeAdapter<List**<Object>> adapterList<Object>**
I tried all sorts of additional properties hoping they would solve this issue, but nothing worked.
openapi-generator version
6.6.0
OpenAPI declaration file content or url
WithUnknownData:
type: object
title: WithUnknownData
x-internal: true
properties:
data:
oneOf:
- type: object
- type: array
items: {}
Generation Details
{
"generatorName": "java",
"inputSpec": "docs/openapi.yaml",
"output": "generated-clients/java"
}
Steps to reproduce
Related issues/PRs
Suggest a fix
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the issue with the supplied OpenAPI schema and Java generator configuration from version 6.6.0. Inspect the generated WithUnknownDataData source and the Java generation path that produces its escaped generic types. Done means the generated Java model compiles with the affected schema.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, openapi
- Domain
- api, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100