From 5da964ca2f220c43cef0c4a8ae5175a00c8074e6 Mon Sep 17 00:00:00 2001 From: Andrey Markelov Date: Mon, 2 Nov 2026 22:07:32 -0800 Subject: [PATCH 1/2] fix(java): batch Gson model adapter registration --- .../codegen/languages/JavaClientCodegen.java | 24 +++++++++++++++++ .../Java/libraries/okhttp-gson/JSON.mustache | 13 +++++++++ .../codegen/java/JavaClientCodegenTest.java | 27 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index c2b1a9aa93b0..3a276ca51121 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -66,6 +66,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen implements BeanValidationFeatures, PerformBeanValidationFeatures, GzipFeatures { static final String MEDIA_TYPE = "mediaType"; + private static final int GSON_ADAPTERS_PER_BATCH = 200; private final Logger LOGGER = LoggerFactory.getLogger(JavaClientCodegen.class); @@ -1415,6 +1416,29 @@ public void forceSerializationLibrary(String serializationLibrary) { @Override public Map postProcessSupportingFileData(Map objs) { generateYAMLSpecFile(objs); + if (isLibrary(OKHTTP_GSON)) { + @SuppressWarnings("unchecked") + List models = (List) objs.get("models"); + List adapters = new ArrayList<>(); + if (models != null) { + for (ModelMap model : models) { + CodegenModel m = model.getModel(); + if (m != null && !m.isEnum && !m.hasChildren) { + adapters.add(m); + } + } + } + if (adapters.size() > GSON_ADAPTERS_PER_BATCH) { + List> batches = new ArrayList<>(); + for (int i = 0; i < adapters.size(); i += GSON_ADAPTERS_PER_BATCH) { + Map batch = new HashMap<>(); + batch.put("index", batches.size()); + batch.put("models", adapters.subList(i, Math.min(i + GSON_ADAPTERS_PER_BATCH, adapters.size()))); + batches.add(batch); + } + objs.put("gsonAdapterBatches", batches); + } + } return super.postProcessSupportingFileData(objs); } diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache index e285a2a3ef45..88783c95e805 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/JSON.mustache @@ -117,6 +117,14 @@ public class JSON { return clazz; } + {{#gsonAdapterBatches}} + private static void registerModelAdapters{{index}}(GsonBuilder gsonBuilder) { + {{#models}} + gsonBuilder.registerTypeAdapterFactory(new {{modelPackage}}.{{{classname}}}.CustomTypeAdapterFactory()); + {{/models}} + } + + {{/gsonAdapterBatches}} static { GsonBuilder gsonBuilder = createGson(); gsonBuilder.registerTypeAdapter(Date.class, dateTypeAdapter); @@ -131,6 +139,10 @@ public class JSON { gsonBuilder.registerTypeAdapter(LocalDateTime.class, localDateTimeTypeAdapter); {{/jsr310}} gsonBuilder.registerTypeAdapter(byte[].class, byteArrayAdapter); + {{#gsonAdapterBatches}} + registerModelAdapters{{index}}(gsonBuilder); + {{/gsonAdapterBatches}} + {{^gsonAdapterBatches}} {{#models}} {{#model}} {{^isEnum}} @@ -140,6 +152,7 @@ public class JSON { {{/isEnum}} {{/model}} {{/models}} + {{/gsonAdapterBatches}} gson = gsonBuilder.create(); } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java index 256c2203428e..a212aabcf0e3 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java @@ -81,6 +81,33 @@ public class JavaClientCodegenTest { private static final String JAVA_GENERATOR = "java"; + @Test + @SneakyThrows + public void testOkHttpGsonBatchesModelAdapterRegistrations() { + Path output = newTempFolder(); + Path spec = output.resolve("many-models.yaml"); + StringBuilder yaml = new StringBuilder("openapi: 3.0.0\ninfo: {title: Batch test, version: '1'}\npaths: {}\ncomponents:\n schemas:\n"); + for (int i = 0; i < 201; i++) { + yaml.append(" Model").append(i).append(":\n type: object\n properties:\n value: {type: string}\n"); + } + Files.write(spec, yaml.toString().getBytes(StandardCharsets.UTF_8)); + CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName(JAVA_GENERATOR) + .setLibrary(OKHTTP_GSON) + .setInputSpec(spec.toString()) + .setOutputDir(output.toString()); + new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + Path json = output.resolve("src/main/java/org/openapitools/client/JSON.java"); + String code = new String(Files.readAllBytes(json), StandardCharsets.UTF_8); + StaticJavaParser.parse(code); + assertThat(code).contains("private static void registerModelAdapters0(GsonBuilder gsonBuilder)", + "private static void registerModelAdapters1(GsonBuilder gsonBuilder)", + "registerModelAdapters0(gsonBuilder);", "registerModelAdapters1(gsonBuilder);"); + for (int i = 0; i < 201; i++) { + assertThat(code).containsOnlyOnce("new org.openapitools.client.model.Model" + i + ".CustomTypeAdapterFactory()"); + } + } + // This is the kind of information that ideally would be defined and available system-wide @Getter enum Library { From 39693a6143515e90e0236e4733d26491a5355a9c Mon Sep 17 00:00:00 2001 From: Andrey Markelov Date: Tue, 3 Nov 2026 07:14:26 -0800 Subject: [PATCH 2/2] docs(java): explain Gson adapter batching --- .../org/openapitools/codegen/languages/JavaClientCodegen.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index 3a276ca51121..e2c37a46b509 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -1417,6 +1417,7 @@ public void forceSerializationLibrary(String serializationLibrary) { public Map postProcessSupportingFileData(Map objs) { generateYAMLSpecFile(objs); if (isLibrary(OKHTTP_GSON)) { + // Match the models registered by JSON.mustache, preserving their generation order. @SuppressWarnings("unchecked") List models = (List) objs.get("models"); List adapters = new ArrayList<>(); @@ -1428,6 +1429,7 @@ public Map postProcessSupportingFileData(Map obj } } } + // Large initializers can exceed the JVM method-size limit; register in bounded batches. if (adapters.size() > GSON_ADAPTERS_PER_BATCH) { List> batches = new ArrayList<>(); for (int i = 0; i < adapters.size(); i += GSON_ADAPTERS_PER_BATCH) {