diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index 3e355713e7f9..2905c1338e5c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -969,7 +969,32 @@ public static boolean shouldGenerateMapModel(Schema schema) { } public static boolean shouldGenerateArrayModel(Schema schema) { - return ModelUtils.isGenerateAliasAsModel(schema) || !(schema.getProperties() == null || schema.getProperties().isEmpty()); + if (ModelUtils.isGenerateAliasAsModel(schema)) { + return true; + } + // Generate if the array schema itself has properties (unusual but valid) + if (schema.getProperties() != null && !schema.getProperties().isEmpty()) { + return true; + } + // Generate if items are non-trivial (not a simple primitive alias like List). + // After InlineModelResolver runs, complex inline items are extracted to components + // and replaced with a $ref, so we check for $ref as well as inline properties. + Schema items = schema.getItems(); + if (items != null) { + // Items resolved to a $ref (inline object was extracted to components) + if (items.get$ref() != null && !items.get$ref().isEmpty()) { + return true; + } + // Items with inline properties + if (items.getProperties() != null && !items.getProperties().isEmpty()) { + return true; + } + // Items with composed schema (oneOf/anyOf/allOf) + if (ModelUtils.isComposedSchema(items)) { + return true; + } + } + return false; } /** 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 8f64f07e2c38..a349a3882c21 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 @@ -598,7 +598,7 @@ public void testJdkHttpClientWithAndWithoutDiscriminator() { generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true"); List files = generator.opts(configurator.toClientOptInput()).generate(); - assertThat(files).hasSize(153); + assertThat(files).hasSize(156); validateJavaSourceFiles(files); assertThat(output.resolve("src/main/java/xyz/abcdef/model/Dog.java")).content() .contains("import xyz.abcdef.invoker.JSON;"); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java index 48f1340cf137..eab739e72e5b 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java @@ -724,4 +724,60 @@ public void getParentNameMultipleInterfacesTest() { Schema composedSchema = allSchemas.get("RandomAnimalsResponse_animals_inner"); assertNull(ModelUtils.getParentName(composedSchema, allSchemas)); } + + @Test + public void shouldGenerateArrayModelWithRefItems() { + // array with items.$ref should generate a model (most common case after InlineModelResolver) + Schema items = new Schema<>(); + items.set$ref("#/components/schemas/Foo"); + Schema arraySchema = new ArraySchema().items(items); + assertTrue(ModelUtils.shouldGenerateArrayModel(arraySchema)); + } + + @Test + public void shouldNotGenerateArrayModelWithPrimitiveItems() { + // array with simple string items is a plain alias (e.g. List) — skip + Schema arraySchema = new ArraySchema().items(new StringSchema()); + assertFalse(ModelUtils.shouldGenerateArrayModel(arraySchema)); + } + + @Test + public void shouldNotGenerateArrayModelWithPrimitiveIntegerItems() { + // array with simple integer items is a plain alias (e.g. List) — skip + Schema arraySchema = new ArraySchema().items(new IntegerSchema()); + assertFalse(ModelUtils.shouldGenerateArrayModel(arraySchema)); + } + + @Test + public void shouldGenerateArrayModelWithComposedItems() { + // array with oneOf items should generate a model + Schema items = new Schema<>(); + items.oneOf(List.of(new Schema<>().$ref("#/components/schemas/A"), new Schema<>().$ref("#/components/schemas/B"))); + Schema arraySchema = new ArraySchema().items(items); + assertTrue(ModelUtils.shouldGenerateArrayModel(arraySchema)); + } + + @Test + public void shouldGenerateArrayModelWithInlineObjectItems() { + // array with items that have inline properties should generate a model + Schema items = new ObjectSchema(); + items.addProperty("name", new StringSchema()); + Schema arraySchema = new ArraySchema().items(items); + assertTrue(ModelUtils.shouldGenerateArrayModel(arraySchema)); + } + + @Test + public void shouldNotGenerateArrayModelWithNullItems() { + // array with no items at all — alias, skip + Schema arraySchema = new ArraySchema(); + assertFalse(ModelUtils.shouldGenerateArrayModel(arraySchema)); + } + + @Test + public void shouldGenerateArrayModelWhenGenerateAliasAsModelExtensionIsSet() { + // with x-generate-alias-as-model extension, even a simple alias should generate + Schema arraySchema = new ArraySchema().items(new StringSchema()); + arraySchema.addExtension("x-generate-alias-as-model", true); + assertTrue(ModelUtils.shouldGenerateArrayModel(arraySchema)); + } }