diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache index dc71a8beb320..7e1e01987284 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/oneof_model.mustache @@ -81,9 +81,16 @@ public class {{classname}} extends AbstractOpenApiSchema{{#vendorExtensions.x-im if (value.getActualInstance() instanceof {{#isArray}}List{{/isArray}}{{#isMap}}Map{{/isMap}}{{^isMap}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isMap}}) { {{#isPrimitiveType}} {{^isMap}} + {{#isArray}} + JsonArray array = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonArray(); + elementAdapter.write(out, array); + return; + {{/isArray}} + {{^isArray}} JsonPrimitive primitive = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonPrimitive(); elementAdapter.write(out, primitive); return; + {{/isArray}} {{/isMap}} {{#isMap}} JsonObject object = adapter{{#sanitizeDataType}}{{{dataType}}}{{/sanitizeDataType}}.toJsonTree(({{{dataType}}})value.getActualInstance()).getAsJsonObject(); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaOkHttpGsonOneOfPrimitiveArrayTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaOkHttpGsonOneOfPrimitiveArrayTest.java new file mode 100644 index 000000000000..aef7d881c373 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaOkHttpGsonOneOfPrimitiveArrayTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2026 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.openapitools.codegen.java; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapterFactory; +import org.openapitools.codegen.DefaultGenerator; +import org.openapitools.codegen.config.CodegenConfigurator; +import org.testng.annotations.Test; + +import javax.tools.ToolProvider; +import java.io.ByteArrayOutputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import static org.openapitools.codegen.TestUtils.newTempFolder; +import static org.testng.Assert.*; + +public class JavaOkHttpGsonOneOfPrimitiveArrayTest { + @Test + public void testPrimitiveArrayRoundTrip() throws Exception { + Path output = newTempFolder(); + CodegenConfigurator config = new CodegenConfigurator().setGeneratorName("java").setLibrary("okhttp-gson") + .setInputSpec("src/test/resources/3_0/java/oneof-primitive-array.yaml").setOutputDir(output.toString()) + .addGlobalProperty("models", "").addGlobalProperty("modelDocs", "false") + .addGlobalProperty("modelTests", "false"); + new DefaultGenerator().opts(config.toClientOptInput()).generate(); + ClassOrInterfaceDeclaration union = StaticJavaParser.parse(output.resolve("src/main/java/org/openapitools/client/model/Union.java")) + .getClassByName("Union").orElseThrow(); + ClassOrInterfaceDeclaration factory = union.getMembers().stream() + .filter(m -> m instanceof ClassOrInterfaceDeclaration).map(m -> (ClassOrInterfaceDeclaration) m) + .filter(c -> c.getNameAsString().equals("CustomTypeAdapterFactory")).findFirst().orElseThrow(); + + // Exercise the generated factory with a minimal model stub and the real Gson runtime. + String fixture = "import com.google.gson.*;\nimport com.google.gson.reflect.*;\n" + + "import com.google.gson.stream.*;\nimport java.io.*;\nimport java.lang.reflect.Type;\n" + + "import java.util.*;\nimport java.util.logging.*;\n" + + "public class UnionFixture { public static class Union {\n" + + "private static final Logger log = Logger.getLogger(Union.class.getName());\n" + + "private Object value; public Object getActualInstance() { return value; }\n" + + "public void setActualInstance(Object value) { this.value = value; }\n" + + factory + "\n" + + "public static class Variant { public String kind; " + + "public static void validateJsonElement(JsonElement e) throws IOException { " + + "if (!e.isJsonObject() || !e.getAsJsonObject().has(\"kind\")) throw new IOException(); } }\n" + + "} }\n"; + Path source = output.resolve("UnionFixture.java"); + Files.writeString(source, fixture); + String classpath = Paths.get(Gson.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toString(); + ByteArrayOutputStream diagnostics = new ByteArrayOutputStream(); + assertNotNull(ToolProvider.getSystemJavaCompiler()); + assertEquals(ToolProvider.getSystemJavaCompiler().run(null, diagnostics, diagnostics, + "--release", "8", "-classpath", classpath, "-d", output.toString(), source.toString()), + 0, diagnostics.toString(StandardCharsets.UTF_8)); + try (URLClassLoader loader = new URLClassLoader(new URL[]{output.toUri().toURL()}, getClass().getClassLoader())) { + Class type = loader.loadClass("UnionFixture$Union"); + TypeAdapterFactory instance = (TypeAdapterFactory) loader.loadClass("UnionFixture$Union$CustomTypeAdapterFactory") + .getConstructor().newInstance(); + Gson gson = new GsonBuilder().registerTypeAdapterFactory(instance).create(); + String json = "[\"alpha\",\"beta\"]"; + Object result = gson.fromJson(json, type); + assertTrue(type.getMethod("getActualInstance").invoke(result) instanceof List); + assertEquals(gson.toJson(result, type), json); + json = "{\"kind\":\"known\"}"; + result = gson.fromJson(json, type); + assertEquals(type.getMethod("getActualInstance").invoke(result).getClass().getSimpleName(), "Variant"); + assertEquals(gson.toJson(result, type), json); + } + } +} diff --git a/modules/openapi-generator/src/test/resources/3_0/java/oneof-primitive-array.yaml b/modules/openapi-generator/src/test/resources/3_0/java/oneof-primitive-array.yaml new file mode 100644 index 000000000000..992d23947cb7 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/java/oneof-primitive-array.yaml @@ -0,0 +1,19 @@ +openapi: 3.0.3 +info: + title: oneOf primitive array + version: '1' +paths: {} +components: + schemas: + Union: + oneOf: + - $ref: '#/components/schemas/Variant' + - type: array + items: + type: string + Variant: + type: object + required: [kind] + properties: + kind: + type: string diff --git a/samples/client/petstore/java/okhttp-gson-3.1/src/main/java/org/openapitools/client/model/RefToRefParameterAnyofRefToAnyofParameter.java b/samples/client/petstore/java/okhttp-gson-3.1/src/main/java/org/openapitools/client/model/RefToRefParameterAnyofRefToAnyofParameter.java index 3c5843ad08a7..128672029663 100644 --- a/samples/client/petstore/java/okhttp-gson-3.1/src/main/java/org/openapitools/client/model/RefToRefParameterAnyofRefToAnyofParameter.java +++ b/samples/client/petstore/java/okhttp-gson-3.1/src/main/java/org/openapitools/client/model/RefToRefParameterAnyofRefToAnyofParameter.java @@ -84,8 +84,8 @@ public void write(JsonWriter out, RefToRefParameterAnyofRefToAnyofParameter valu } // check if the actual instance is of the type `List` if (value.getActualInstance() instanceof List) { - JsonPrimitive primitive = adapterListString.toJsonTree((List)value.getActualInstance()).getAsJsonPrimitive(); - elementAdapter.write(out, primitive); + JsonArray array = adapterListString.toJsonTree((List)value.getActualInstance()).getAsJsonArray(); + elementAdapter.write(out, array); return; } throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: List, String"); diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/ArrayOneOf.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/ArrayOneOf.java index c3a0de6127d4..410abce49676 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/ArrayOneOf.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/ArrayOneOf.java @@ -84,8 +84,8 @@ public void write(JsonWriter out, ArrayOneOf value) throws IOException { } // check if the actual instance is of the type `List` if (value.getActualInstance() instanceof List) { - JsonPrimitive primitive = adapterListString.toJsonTree((List)value.getActualInstance()).getAsJsonPrimitive(); - elementAdapter.write(out, primitive); + JsonArray array = adapterListString.toJsonTree((List)value.getActualInstance()).getAsJsonArray(); + elementAdapter.write(out, array); return; } throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: Integer, List"); diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/FakeOneOfWIthSameErasureGet200Response.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/FakeOneOfWIthSameErasureGet200Response.java index 3fb7cc820635..300294bce438 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/FakeOneOfWIthSameErasureGet200Response.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/FakeOneOfWIthSameErasureGet200Response.java @@ -80,8 +80,8 @@ public void write(JsonWriter out, FakeOneOfWIthSameErasureGet200Response value) // check if the actual instance is of the type `List` if (value.getActualInstance() instanceof List) { - JsonPrimitive primitive = adapterListString.toJsonTree((List)value.getActualInstance()).getAsJsonPrimitive(); - elementAdapter.write(out, primitive); + JsonArray array = adapterListString.toJsonTree((List)value.getActualInstance()).getAsJsonArray(); + elementAdapter.write(out, array); return; } throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: List, List");