From 79f698b1b45d94cf2ee867c7728326a01c1b4dab Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sun, 30 Aug 2026 18:43:43 +0200 Subject: [PATCH 01/26] issue_24769 sample --- .../test/resources/3_0/oneOf_issue_24769.yaml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml diff --git a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml new file mode 100644 index 000000000000..8cf38f1bcbc9 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml @@ -0,0 +1,51 @@ +openapi: 3.1.0 +info: + title: discriminator_constants + version: '1.0.0' +components: + schemas: + Pet: + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + discriminator: + propertyName: type + mapping: + CAT: '#/components/schemas/Cat' + DOG: '#/components/schemas/Dog' + Cat: + description: A representation of a cat + type: object + properties: + type: + type: string + description: pet is a CAT + const: CAT + huntingSkill: + type: string + description: The measured skill for hunting + enum: + - clueless + - lazy + - adventurous + - aggressive + required: + - type + - huntingSkill + Dog: + description: A representation of a dog + type: object + properties: + type: + type: string + description: Pet is a DOG + const: DOG + packSize: + type: integer + format: int32 + description: the size of the pack the dog is from + default: 0 + minimum: 0 + required: + - type + - packSize From e0e2b812b68fcc0fdcaba9f59a4a9291797cc239 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sun, 30 Aug 2026 18:52:04 +0200 Subject: [PATCH 02/26] Fix #24769: improve common discriminator getter --- .../openapitools/codegen/DefaultCodegen.java | 44 ++++++++++++++- .../codegen/OpenAPINormalizer.java | 42 ++++----------- .../languages/AbstractJavaCodegen.java | 1 + .../codegen/utils/DiscriminatorUtils.java | 26 +++++++++ .../codegen/utils/ModelUtils.java | 54 +++++++++++++++++++ .../java/spring/SpringCodegenTest.java | 19 +++++++ 6 files changed, 152 insertions(+), 34 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index e8f7eb86e02a..a6f529da7c4d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3869,9 +3869,49 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch * @param discriminatorPropertyName The name of the discriminator property. */ protected String getDiscriminatorPropertyType(Schema schema, String discriminatorPropertyName) { - return DiscriminatorUtils.getDiscriminatorPropertyType(schema, discriminatorPropertyName) + String type = DiscriminatorUtils.getDiscriminatorPropertyType(schema, discriminatorPropertyName) .map(this::toModelName) - .orElseGet(() -> typeMapping.get("string")); + .orElse(null); + if (type != null) { + return type; + } + List schemas = DiscriminatorUtils.getDistinctTypes(openAPI, schema, discriminatorPropertyName); + return getCommonSchemaType(schemas); + } + + /** + * get the most commons denominator schemaType for several schemas. + * + * @param schemas the list of schemas to compare. + * + * @Return the comman type + */ + protected String getCommonSchemaType(List schemas) { + switch (schemas.size()) { + case 0: + return typeMapping.get("object"); + case 1: + return getSchemaType(schemas.get(0)); + default: + break; + } + String simpleType = "object"; + if (schemas.stream().allMatch(ModelUtils::isEnumSchema)) { + simpleType = "enum"; + } else if (schemas.stream().allMatch(ModelUtils::isIntegerSchema)) { + simpleType = "integer"; + } else if (schemas.stream().allMatch(ModelUtils::isLongSchema)) { + simpleType = "long"; + } else if (schemas.stream().allMatch(ModelUtils::isNumberSchema)) { + simpleType = "number"; + } + + String type = typeMapping.get(simpleType); + if (type == null) { + // some typeMapping are not defined. Fallback to object. + return typeMapping.get("object"); + } + return type; } /** diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index a684bf326683..8ca960feff21 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -1866,6 +1866,14 @@ protected String getDiscriminatorValue(String refSchema, String discriminatorPro return schemaName; } + + /** + * find a property under the schema + */ + protected Schema findProperty(Schema schema, String propertyName) { + return findProperty(schema, propertyName, false, new HashSet<>()); + } + /** * find a property under the schema. * @@ -1875,38 +1883,8 @@ protected String getDiscriminatorValue(String refSchema, String discriminatorPro * @param visitedSchemas avoid infinite recursion * @return found property or null if not found. */ - private Schema findProperty(Schema schema, String propertyName, boolean toDelete, Set visitedSchemas) { - schema = ModelUtils.getReferencedSchema(openAPI, schema); - if (propertyName == null || schema == null || visitedSchemas.contains(schema)) { - return null; - } - visitedSchemas.add(schema); - Map properties = schema.getProperties(); - if (properties != null) { - Schema property = ModelUtils.getReferencedSchema(openAPI, properties.get(propertyName)); - if (property != null) { - if (toDelete) { - if (schema.getProperties().remove(propertyName) != null) { - LOGGER.info("property " + propertyName + " has been removed in REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING normalization"); - if (schema.getProperties().isEmpty()) { - schema.setProperties(null); - } - } - } - return property; - } - } - List allOfs = schema.getAllOf(); - if (allOfs != null) { - for (Schema child : allOfs) { - Schema found = findProperty(child, propertyName, toDelete, visitedSchemas); - if (found != null) { - return found; - } - } - } - - return null; + protected Schema findProperty(Schema schema, String propertyName, boolean toDelete, Set visitedSchemas) { + return ModelUtils.findProperty(openAPI, schema, propertyName, toDelete, visitedSchemas); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index 554f98183fc7..12f6784baebe 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -304,6 +304,7 @@ public AbstractJavaCodegen() { typeMapping.put("date", "Date"); typeMapping.put("file", "File"); typeMapping.put("AnyType", "Object"); + typeMapping.put("enum", "Enum"); importMapping.put("BigDecimal", "java.math.BigDecimal"); importMapping.put("UUID", "java.util.UUID"); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 204db9346329..c99ff8b457c7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -10,6 +10,7 @@ import org.slf4j.helpers.MessageFormatter; import java.util.*; +import java.util.stream.Collectors; import static org.openapitools.codegen.CodegenConstants.X_DISCRIMINATOR_VALUE; import static org.openapitools.codegen.utils.OnceLogger.once; @@ -322,6 +323,31 @@ private static CodegenProperty getDiscriminatorCodegenProperty(OpenAPI openAPI, return null; } + /** + * Get the best matching simple type for all the mapped schemas + * @param openAPI + * @param schema The Schema that may contain the discriminator + * @param discPropName The String that is the discriminator propertyName in the schema + * @return "enum", "string" or "object + */ + public static List getDistinctTypes(OpenAPI openAPI, Schema schema, String discPropName) { + List mappedSchemas = getMappedSchemas(openAPI, schema); + return mappedSchemas.stream().map(sc -> ModelUtils.findProperty(openAPI, sc, discPropName)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { + if (schema.getDiscriminator() != null && schema.getDiscriminator().getMapping() != null) { + return schema.getDiscriminator().getMapping().values().stream() + .map(ref -> ModelUtils.getSchema(openAPI, ModelUtils.getSimpleRef(ref))) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + + } + public static class DiscriminatorData { private final Discriminator discriminator; 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 fe612c09dcd9..53878a7a1e74 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 @@ -2990,4 +2990,58 @@ public LinkedHashSet build() { } } } + + /** + * find a property under the schema. + * + * @param schema + * @param propertyName property to find + * @return found property or null if not found. + */ + public static Schema findProperty(OpenAPI openAPI, Schema schema, String propertyName) { + return findProperty(openAPI, schema, propertyName, false, new HashSet<>()); + } + + /** + * find a property under the schema. + * + * @param schema + * @param propertyName property to find + * @param toDelete if true delete the found property + * @param visitedSchemas avoid infinite recursion + * @return found property or null if not found. + */ + public static Schema findProperty(OpenAPI openAPI, Schema schema, String propertyName, boolean toDelete, Set visitedSchemas) { + schema = ModelUtils.getReferencedSchema(openAPI, schema); + if (propertyName == null || schema == null || visitedSchemas.contains(schema)) { + return null; + } + visitedSchemas.add(schema); + Map properties = schema.getProperties(); + if (properties != null) { + Schema property = ModelUtils.getReferencedSchema(openAPI, properties.get(propertyName)); + if (property != null) { + if (toDelete) { + if (schema.getProperties().remove(propertyName) != null) { + LOGGER.info("property " + propertyName + " has been removed in REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING normalization"); + if (schema.getProperties().isEmpty()) { + schema.setProperties(null); + } + } + } + return property; + } + } + List allOfs = schema.getAllOf(); + if (allOfs != null) { + for (Schema child : allOfs) { + Schema found = findProperty(openAPI, child, propertyName, toDelete, visitedSchemas); + if (found != null) { + return found; + } + } + } + + return null; + } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index b7184f853396..f9ca223a7e5d 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9335,4 +9335,23 @@ public void issue_24232() throws IOException { JavaFileAssert.assertThat(files.get("Dummy.java")) .fileContains("import org.myorg.MyCustomId;", "import org.myorg.MyCustomKey;"); } + + @Test + public void issue_24769() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/oneOf_issue_24769.yaml", SPRING_BOOT, + Map.of(USE_SPRING_BOOT4, true) + ); + + JavaFileAssert.assertThat(files.get("Dog.java")) + .fileContains("public enum TypeEnum {", + "DOG(\"DOG\");"); + JavaFileAssert.assertThat(files.get("Cat.java")) + .fileContains("public enum TypeEnum {", + "CAT(\"CAT\");"); + JavaFileAssert.assertThat(files.get("Pet.java")) + .fileDoesNotContain("public enum TypeEnum {") + .fileContains("public Enum getType();"); + + } } From 460bbd700f399fe1e5172cb22b51096e51594457 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sun, 30 Aug 2026 19:12:04 +0200 Subject: [PATCH 03/26] Fix #24769: common string schema with no format --- .../main/java/org/openapitools/codegen/DefaultCodegen.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index a6f529da7c4d..0c40ff1801bb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3902,8 +3902,12 @@ protected String getCommonSchemaType(List schemas) { simpleType = "integer"; } else if (schemas.stream().allMatch(ModelUtils::isLongSchema)) { simpleType = "long"; + } else if (schemas.stream().allMatch(ModelUtils::isShortSchema)) { + simpleType = "short"; } else if (schemas.stream().allMatch(ModelUtils::isNumberSchema)) { simpleType = "number"; + } else if (schemas.stream().allMatch(s -> ModelUtils.isStringSchema(s) && s.getFormat() == null)) { + return "string"; } String type = typeMapping.get(simpleType); From b87497eca8684b13a664a878f3ec1d6f453bdf70 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 08:21:19 +0200 Subject: [PATCH 04/26] workaround for some misconfigured unit tests --- .../java/org/openapitools/codegen/DefaultCodegen.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 0c40ff1801bb..4190bd0040c0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3891,7 +3891,14 @@ protected String getCommonSchemaType(List schemas) { case 0: return typeMapping.get("object"); case 1: - return getSchemaType(schemas.get(0)); + Schema first = schemas.get(0); + try { + return getSchemaType(first); + } catch (Exception e) { + // fallback for some unit test misconfigurations... + LOGGER.warn("Unable to getSchemaType for " + first, e); + return typeMapping.get("string"); + } default: break; } From 76578db92aaea0b7fbb9ff387acf99d049394ecf Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 08:40:46 +0200 Subject: [PATCH 05/26] Fallback to default behaviour for Kotlin --- .../codegen/languages/KotlinClientCodegen.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java index 00e523fd449f..636b2e5cde2b 100755 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java @@ -18,6 +18,7 @@ package org.openapitools.codegen.languages; import com.samskivert.mustache.Mustache; +import io.swagger.v3.oas.models.media.Schema; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.StringUtils; @@ -1009,6 +1010,16 @@ public ModelsMap postProcessModels(ModelsMap objs) { return objects; } + /** + * Kotlin has its own implementation in postProcessAllModels + * + * @return + */ + @Override + protected String getCommonSchemaType(List schemas) { + return typeMapping.get("string"); + } + @Override public Map postProcessAllModels(Map objs) { objs = super.postProcessAllModels(objs); From 9295e12f9ec8404eaa84e590864da3a5f7ea9540 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 09:22:42 +0200 Subject: [PATCH 06/26] Fallback to default behaviour for Kotlin --- .../java/org/openapitools/codegen/DefaultCodegen.java | 4 ++-- .../codegen/languages/AbstractKotlinCodegen.java | 10 ++++++++++ .../codegen/languages/KotlinClientCodegen.java | 11 ----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 4190bd0040c0..2deeb681bd74 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3880,8 +3880,8 @@ protected String getDiscriminatorPropertyType(Schema schema, String discriminato } /** - * get the most commons denominator schemaType for several schemas. - * + * Get the most commons denominator schemaType for several schemas. + *

* @param schemas the list of schemas to compare. * * @Return the comman type diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java index d62c812ad86e..52fafa631674 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java @@ -490,6 +490,16 @@ public Map postProcessAllModels(Map objs) return objs; } + /** + * Kotlin has its own implementation in postProcessAllModels + * + * @return + */ + @Override + protected String getCommonSchemaType(List schemas) { + return typeMapping.get("string"); + } + @Override public ModelsMap postProcessModels(ModelsMap objs) { objs = super.postProcessModelsEnum(objs); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java index 636b2e5cde2b..00e523fd449f 100755 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java @@ -18,7 +18,6 @@ package org.openapitools.codegen.languages; import com.samskivert.mustache.Mustache; -import io.swagger.v3.oas.models.media.Schema; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.StringUtils; @@ -1010,16 +1009,6 @@ public ModelsMap postProcessModels(ModelsMap objs) { return objects; } - /** - * Kotlin has its own implementation in postProcessAllModels - * - * @return - */ - @Override - protected String getCommonSchemaType(List schemas) { - return typeMapping.get("string"); - } - @Override public Map postProcessAllModels(Map objs) { objs = super.postProcessAllModels(objs); From 0e5839f87062f9b03afaebf8ee6e719998a31243 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 11:54:28 +0200 Subject: [PATCH 07/26] Fallback to string if not type --- .../src/main/java/org/openapitools/codegen/DefaultCodegen.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 2deeb681bd74..229f4e5c60ec 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3889,7 +3889,8 @@ protected String getDiscriminatorPropertyType(Schema schema, String discriminato protected String getCommonSchemaType(List schemas) { switch (schemas.size()) { case 0: - return typeMapping.get("object"); + //. keep string for backward compatibility + return typeMapping.get("string"); case 1: Schema first = schemas.get(0); try { From 97fcea6be6f861f68e9ed489a9c1012d9e8c49d8 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 15:15:36 +0200 Subject: [PATCH 08/26] Hanling of $ref to find discriminatorType --- .../openapitools/codegen/DefaultCodegen.java | 8 ++ .../codegen/OpenAPINormalizer.java | 34 +++++++- .../codegen/utils/DiscriminatorUtils.java | 31 ++++++- .../codegen/utils/ModelUtils.java | 43 ---------- .../codegen/java/JavaClientCodegenTest.java | 19 +++++ .../java/spring/SpringCodegenTest.java | 5 ++ .../test/resources/3_0/oneOf_issue_24769.yaml | 80 ++++++++++++++++--- 7 files changed, 163 insertions(+), 57 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 229f4e5c60ec..74bddd267321 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3894,6 +3894,13 @@ protected String getCommonSchemaType(List schemas) { case 1: Schema first = schemas.get(0); try { + if (StringUtils.isEmpty(first.get$ref())) { + Schema refScheme = ModelUtils.getReferencedSchema(openAPI, first); + if (ModelUtils.isEnumSchema(refScheme)) { + return toModelName(first.getName()); + } + return getSchemaType(refScheme); + } return getSchemaType(first); } catch (Exception e) { // fallback for some unit test misconfigurations... @@ -3903,6 +3910,7 @@ protected String getCommonSchemaType(List schemas) { default: break; } + schemas = schemas.stream().map(s -> ModelUtils.getReferencedSchema(openAPI, s)).collect(Collectors.toList()); String simpleType = "object"; if (schemas.stream().allMatch(ModelUtils::isEnumSchema)) { simpleType = "enum"; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index 8ca960feff21..de10109cab8d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -1883,8 +1883,38 @@ protected Schema findProperty(Schema schema, String propertyName) { * @param visitedSchemas avoid infinite recursion * @return found property or null if not found. */ - protected Schema findProperty(Schema schema, String propertyName, boolean toDelete, Set visitedSchemas) { - return ModelUtils.findProperty(openAPI, schema, propertyName, toDelete, visitedSchemas); + private Schema findProperty(Schema schema, String propertyName, boolean toDelete, Set visitedSchemas) { + schema = ModelUtils.getReferencedSchema(openAPI, schema); + if (propertyName == null || schema == null || visitedSchemas.contains(schema)) { + return null; + } + visitedSchemas.add(schema); + Map properties = schema.getProperties(); + if (properties != null) { + Schema property = ModelUtils.getReferencedSchema(openAPI, properties.get(propertyName)); + if (property != null) { + if (toDelete) { + if (schema.getProperties().remove(propertyName) != null) { + LOGGER.info("property " + propertyName + " has been removed in REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING normalization"); + if (schema.getProperties().isEmpty()) { + schema.setProperties(null); + } + } + } + return property; + } + } + List allOfs = schema.getAllOf(); + if (allOfs != null) { + for (Schema child : allOfs) { + Schema found = findProperty(child, propertyName, toDelete, visitedSchemas); + if (found != null) { + return found; + } + } + } + + return null; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index c99ff8b457c7..520d8928923c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -332,11 +332,13 @@ private static CodegenProperty getDiscriminatorCodegenProperty(OpenAPI openAPI, */ public static List getDistinctTypes(OpenAPI openAPI, Schema schema, String discPropName) { List mappedSchemas = getMappedSchemas(openAPI, schema); - return mappedSchemas.stream().map(sc -> ModelUtils.findProperty(openAPI, sc, discPropName)) + return mappedSchemas.stream().map(sc -> findProperty(openAPI, sc, discPropName, new HashSet<>())) + .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); } + public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { if (schema.getDiscriminator() != null && schema.getDiscriminator().getMapping() != null) { return schema.getDiscriminator().getMapping().values().stream() @@ -345,9 +347,36 @@ public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { .collect(Collectors.toList()); } return Collections.emptyList(); + } + + public static Schema findProperty(OpenAPI openAPI, Schema schema, String propertyName, Set visitedSchemas) { + schema = ModelUtils.getReferencedSchema(openAPI, schema); + if (propertyName == null || schema == null || visitedSchemas.contains(schema)) { + return null; + } + visitedSchemas.add(schema); + Map properties = schema.getProperties(); + if (properties != null) { + Schema property = properties.get(propertyName); +// Schema property = ModelUtils.getReferencedSchema(openAPI, ); + if (property != null) { + return property; + } + } + List allOfs = schema.getAllOf(); + if (allOfs != null) { + for (Schema child : allOfs) { + Schema found = findProperty(openAPI, child, propertyName, visitedSchemas); + if (found != null) { + return found; + } + } + } + return null; } + public static class DiscriminatorData { private final Discriminator discriminator; 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 53878a7a1e74..207b5c36e90b 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 @@ -3001,47 +3001,4 @@ public LinkedHashSet build() { public static Schema findProperty(OpenAPI openAPI, Schema schema, String propertyName) { return findProperty(openAPI, schema, propertyName, false, new HashSet<>()); } - - /** - * find a property under the schema. - * - * @param schema - * @param propertyName property to find - * @param toDelete if true delete the found property - * @param visitedSchemas avoid infinite recursion - * @return found property or null if not found. - */ - public static Schema findProperty(OpenAPI openAPI, Schema schema, String propertyName, boolean toDelete, Set visitedSchemas) { - schema = ModelUtils.getReferencedSchema(openAPI, schema); - if (propertyName == null || schema == null || visitedSchemas.contains(schema)) { - return null; - } - visitedSchemas.add(schema); - Map properties = schema.getProperties(); - if (properties != null) { - Schema property = ModelUtils.getReferencedSchema(openAPI, properties.get(propertyName)); - if (property != null) { - if (toDelete) { - if (schema.getProperties().remove(propertyName) != null) { - LOGGER.info("property " + propertyName + " has been removed in REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING normalization"); - if (schema.getProperties().isEmpty()) { - schema.setProperties(null); - } - } - } - return property; - } - } - List allOfs = schema.getAllOf(); - if (allOfs != null) { - for (Schema child : allOfs) { - Schema found = findProperty(openAPI, child, propertyName, toDelete, visitedSchemas); - if (found != null) { - return found; - } - } - } - - return null; - } } 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 5d5b31c25b51..0559968ea180 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 @@ -5026,4 +5026,23 @@ private static JavaClientCodegen newRetrofit2Codegen(Map propert codegen.additionalProperties().putAll(properties); return codegen; } + + + @Test + public void issue_24769() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_1/oneof_polymorphism_and_inheritance.yaml", RESTCLIENT, + Map.of(USE_SEALED_ONE_OF_INTERFACES, true, + USE_ONE_OF_INTERFACES, true) + ); + JavaFileAssert.assertThat("BarOrRefValue.java").fileContains( + "public String getAtType();" + ); + + JavaFileAssert.assertThat("Entity.java").fileContains( + "protected String atType;" + ); + } + + } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index f9ca223a7e5d..dabf5805c018 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9353,5 +9353,10 @@ public void issue_24769() throws IOException { .fileDoesNotContain("public enum TypeEnum {") .fileContains("public Enum getType();"); + JavaFileAssert.assertThat(files.get("PetInteger.java")) + .fileContains("public Integer getIntType();"); + + JavaFileAssert.assertThat(files.get("PetEnumRef.java")) + .fileContains("public PetEnumType getEnumRefType();"); } } diff --git a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml index 8cf38f1bcbc9..3293d2c1c78c 100644 --- a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml @@ -19,16 +19,9 @@ components: properties: type: type: string - description: pet is a CAT const: CAT huntingSkill: type: string - description: The measured skill for hunting - enum: - - clueless - - lazy - - adventurous - - aggressive required: - type - huntingSkill @@ -38,14 +31,79 @@ components: properties: type: type: string - description: Pet is a DOG const: DOG packSize: type: integer format: int32 - description: the size of the pack the dog is from - default: 0 - minimum: 0 required: - type - packSize + PetInteger: + oneOf: + - $ref: '#/components/schemas/CatInteger' + - $ref: '#/components/schemas/DogInteger' + discriminator: + propertyName: intType + mapping: + 1: '#/components/schemas/CatInteger' + 2: '#/components/schemas/DogInteger' + CatInteger: + description: A representation of a cat + type: object + properties: + intType: + type: integer + huntingSkill: + type: string + required: + - intType + - huntingSkill + DogInteger: + description: A representation of a dog + type: object + properties: + intType: + type: integer + packSize: + type: integer + format: int32 + required: + - intType + - packSize + PetEnumRef: + oneOf: + - $ref: '#/components/schemas/CatEnumRef' + - $ref: '#/components/schemas/DogEnumRef' + discriminator: + propertyName: enumRefType + mapping: + CAT: '#/components/schemas/CatEnumRef' + DOG: '#/components/schemas/DogEnumRef' + CatEnumRef: + description: A representation of a cat + type: object + properties: + enumRefType: + $ref: '#/components/schemas/PetEnumType' + huntingSkill: + type: string + required: + - enumRefType + - huntingSkill + DogEnumRef: + description: A representation of a dog + type: object + properties: + enumRefType: + $ref: '#/components/schemas/PetEnumType' + packSize: + type: integer + format: int32 + required: + - enumRefType + - packSize + PetEnumType: + type: string + enum: + - CAT + - DOG \ No newline at end of file From e5c34f7b7fb9a488cf6c4b35c5753dd740d31018 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 15:33:24 +0200 Subject: [PATCH 09/26] cleanup --- .../org/openapitools/codegen/OpenAPINormalizer.java | 8 -------- .../org/openapitools/codegen/utils/ModelUtils.java | 11 ----------- 2 files changed, 19 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index de10109cab8d..a684bf326683 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -1866,14 +1866,6 @@ protected String getDiscriminatorValue(String refSchema, String discriminatorPro return schemaName; } - - /** - * find a property under the schema - */ - protected Schema findProperty(Schema schema, String propertyName) { - return findProperty(schema, propertyName, false, new HashSet<>()); - } - /** * find a property under the schema. * 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 207b5c36e90b..fe612c09dcd9 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 @@ -2990,15 +2990,4 @@ public LinkedHashSet build() { } } } - - /** - * find a property under the schema. - * - * @param schema - * @param propertyName property to find - * @return found property or null if not found. - */ - public static Schema findProperty(OpenAPI openAPI, Schema schema, String propertyName) { - return findProperty(openAPI, schema, propertyName, false, new HashSet<>()); - } } From 4a596b6a5993bc29223f7db97279067ca831b9d8 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 16:50:11 +0200 Subject: [PATCH 10/26] Remove not supported by JavaFileAssert test on sealed interface --- .../codegen/java/JavaClientCodegenTest.java | 19 ------------------- .../java/assertions/JavaFileAssert.java | 3 ++- 2 files changed, 2 insertions(+), 20 deletions(-) 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 0559968ea180..5d5b31c25b51 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 @@ -5026,23 +5026,4 @@ private static JavaClientCodegen newRetrofit2Codegen(Map propert codegen.additionalProperties().putAll(properties); return codegen; } - - - @Test - public void issue_24769() throws IOException { - Map files = generateFromContract( - "src/test/resources/3_1/oneof_polymorphism_and_inheritance.yaml", RESTCLIENT, - Map.of(USE_SEALED_ONE_OF_INTERFACES, true, - USE_ONE_OF_INTERFACES, true) - ); - JavaFileAssert.assertThat("BarOrRefValue.java").fileContains( - "public String getAtType();" - ); - - JavaFileAssert.assertThat("Entity.java").fileContains( - "protected String atType;" - ); - } - - } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/assertions/JavaFileAssert.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/assertions/JavaFileAssert.java index bd9c5b17922d..97df1e81c4cf 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/assertions/JavaFileAssert.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/assertions/JavaFileAssert.java @@ -1,5 +1,6 @@ package org.openapitools.codegen.java.assertions; +import com.github.javaparser.ParseProblemException; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; @@ -36,7 +37,7 @@ public static JavaFileAssert assertThat(final String source) { public static JavaFileAssert assertThat(final Path path) { try { return new JavaFileAssert(StaticJavaParser.parse(path)); - } catch (IOException e) { + } catch (IOException | ParseProblemException e) { throw new RuntimeException("Exception while reading file: " + path, e); } } From 1327168b85198c8e67fbef83b951ebfc5745c483 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 17:56:13 +0200 Subject: [PATCH 11/26] Update modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --- .../java/org/openapitools/codegen/utils/DiscriminatorUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 520d8928923c..0917aef8dda7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -342,7 +342,7 @@ public static List getDistinctTypes(OpenAPI openAPI, Schema schema, Stri public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { if (schema.getDiscriminator() != null && schema.getDiscriminator().getMapping() != null) { return schema.getDiscriminator().getMapping().values().stream() - .map(ref -> ModelUtils.getSchema(openAPI, ModelUtils.getSimpleRef(ref))) + .map(ref -> ModelUtils.getSchema(openAPI, ref.indexOf('/') >= 0 ? ModelUtils.getSimpleRef(ref) : ref)) .filter(Objects::nonNull) .collect(Collectors.toList()); } From 45c00b48a7c209cb55278201ccce0ca5f3e1b67a Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 17:56:30 +0200 Subject: [PATCH 12/26] Update modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --- .../main/java/org/openapitools/codegen/DefaultCodegen.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 74bddd267321..f6780bc5bb3c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3914,12 +3914,12 @@ protected String getCommonSchemaType(List schemas) { String simpleType = "object"; if (schemas.stream().allMatch(ModelUtils::isEnumSchema)) { simpleType = "enum"; - } else if (schemas.stream().allMatch(ModelUtils::isIntegerSchema)) { - simpleType = "integer"; } else if (schemas.stream().allMatch(ModelUtils::isLongSchema)) { simpleType = "long"; } else if (schemas.stream().allMatch(ModelUtils::isShortSchema)) { simpleType = "short"; + } else if (schemas.stream().allMatch(ModelUtils::isIntegerSchema)) { + simpleType = "integer"; } else if (schemas.stream().allMatch(ModelUtils::isNumberSchema)) { simpleType = "number"; } else if (schemas.stream().allMatch(s -> ModelUtils.isStringSchema(s) && s.getFormat() == null)) { From faaf92fadcd62dfb00cd369849785eaf9ce5223e Mon Sep 17 00:00:00 2001 From: jpfinne Date: Mon, 31 Aug 2026 17:57:34 +0200 Subject: [PATCH 13/26] Update modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --- .../src/main/java/org/openapitools/codegen/DefaultCodegen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index f6780bc5bb3c..00562feca325 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3901,7 +3901,7 @@ protected String getCommonSchemaType(List schemas) { } return getSchemaType(refScheme); } - return getSchemaType(first); + return toModelName(ModelUtils.getSimpleRef(first.get$ref())); } catch (Exception e) { // fallback for some unit test misconfigurations... LOGGER.warn("Unable to getSchemaType for " + first, e); From 9d5de48601ec7d2f2005f4253a971bfeb701a3e3 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Tue, 1 Sep 2026 21:21:47 +0200 Subject: [PATCH 14/26] Minor cleanup after code review --- .../codegen/utils/DiscriminatorUtils.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 0917aef8dda7..66556ab9c515 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -324,11 +324,11 @@ private static CodegenProperty getDiscriminatorCodegenProperty(OpenAPI openAPI, } /** - * Get the best matching simple type for all the mapped schemas - * @param openAPI + * Get the best matching simple types for all the mapped schemas + * @param openAPI The openAPI specification * @param schema The Schema that may contain the discriminator * @param discPropName The String that is the discriminator propertyName in the schema - * @return "enum", "string" or "object + * @return the distinct list of property Schema with the requested discPropName */ public static List getDistinctTypes(OpenAPI openAPI, Schema schema, String discPropName) { List mappedSchemas = getMappedSchemas(openAPI, schema); @@ -338,17 +338,27 @@ public static List getDistinctTypes(OpenAPI openAPI, Schema schema, Stri .collect(Collectors.toList()); } - + /** + * return the list of deferenced mapping schemas. + * @return the schemas found or empty list if not found. + */ public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { if (schema.getDiscriminator() != null && schema.getDiscriminator().getMapping() != null) { return schema.getDiscriminator().getMapping().values().stream() - .map(ref -> ModelUtils.getSchema(openAPI, ref.indexOf('/') >= 0 ? ModelUtils.getSimpleRef(ref) : ref)) + .filter(Objects::nonNull) + .map(ref -> ref.indexOf('/') >= 0 ? ModelUtils.getSimpleRef(ref) : ref) + .map(ref -> ModelUtils.getSchema(openAPI, ref)) .filter(Objects::nonNull) .collect(Collectors.toList()); } return Collections.emptyList(); } + /** + * Recursively try to find the schema matching the propertyName. + * + * @return the schema found or null if not found + */ public static Schema findProperty(OpenAPI openAPI, Schema schema, String propertyName, Set visitedSchemas) { schema = ModelUtils.getReferencedSchema(openAPI, schema); if (propertyName == null || schema == null || visitedSchemas.contains(schema)) { @@ -358,7 +368,6 @@ public static Schema findProperty(OpenAPI openAPI, Schema schema, String propert Map properties = schema.getProperties(); if (properties != null) { Schema property = properties.get(propertyName); -// Schema property = ModelUtils.getReferencedSchema(openAPI, ); if (property != null) { return property; } @@ -372,11 +381,9 @@ public static Schema findProperty(OpenAPI openAPI, Schema schema, String propert } } } - return null; } - public static class DiscriminatorData { private final Discriminator discriminator; From 9b2bf73f12696dd0b8aeb72a1f50317ae98e6080 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Tue, 1 Sep 2026 22:18:00 +0200 Subject: [PATCH 15/26] Some variation of #19194 --- .../openapitools/codegen/DefaultCodegen.java | 2 +- .../codegen/utils/DiscriminatorUtils.java | 28 ++++-- .../java/spring/SpringCodegenTest.java | 22 +++++ .../resources/3_0/spring/issue_19194_v2.yaml | 88 +++++++++++++++++++ 4 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/spring/issue_19194_v2.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 00562feca325..a1fd259437da 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3922,7 +3922,7 @@ protected String getCommonSchemaType(List schemas) { simpleType = "integer"; } else if (schemas.stream().allMatch(ModelUtils::isNumberSchema)) { simpleType = "number"; - } else if (schemas.stream().allMatch(s -> ModelUtils.isStringSchema(s) && s.getFormat() == null)) { + } else if (schemas.stream().allMatch(s -> ModelUtils.isStringSchema(s) && s.getFormat() == null && !ModelUtils.isEnumSchema(s))) { return "string"; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 66556ab9c515..4a63c57c71fc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -343,13 +343,27 @@ public static List getDistinctTypes(OpenAPI openAPI, Schema schema, Stri * @return the schemas found or empty list if not found. */ public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { - if (schema.getDiscriminator() != null && schema.getDiscriminator().getMapping() != null) { - return schema.getDiscriminator().getMapping().values().stream() - .filter(Objects::nonNull) - .map(ref -> ref.indexOf('/') >= 0 ? ModelUtils.getSimpleRef(ref) : ref) - .map(ref -> ModelUtils.getSchema(openAPI, ref)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + if (schema.getDiscriminator() != null) { + if (schema.getDiscriminator().getMapping() != null) { + return schema.getDiscriminator().getMapping().values().stream() + .filter(Objects::nonNull) + .map(ref -> ref.indexOf('/') >= 0 ? ModelUtils.getSimpleRef(ref) : ref) + .map(ref -> ModelUtils.getSchema(openAPI, ref)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) { + // try also oneOf without discriminator mapping + List oneOfs = (List) schema.getOneOf(); + return oneOfs.stream() + .filter(Objects::nonNull) + .map(oneOf -> ((Schema)oneOf).get$ref()) + .filter(Objects::nonNull) + .map(ref -> ref.indexOf('/') >= 0 ? ModelUtils.getSimpleRef(ref) : ref) + .map(ref -> ModelUtils.getSchema(openAPI, (String)ref)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } } return Collections.emptyList(); } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index dabf5805c018..bd5f5274f340 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9359,4 +9359,26 @@ public void issue_24769() throws IOException { JavaFileAssert.assertThat(files.get("PetEnumRef.java")) .fileContains("public PetEnumType getEnumRefType();"); } + + @Test + public void issue_19194() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/oneOf_issue_19194.yaml", SPRING_BOOT, + Map.of(USE_SPRING_BOOT4, true), configurator-> + configurator.addInlineSchemaOption("RESOLVE_INLINE_ENUMS", "true") + ); + JavaFileAssert.assertThat(files.get("CargoInterface.java")) + .fileContains("public CargoGeneralParameterUnit getUnit();"); + } + + @Test + public void issue_19194_v2() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/issue_19194_v2.yaml", SPRING_BOOT, + Map.of(USE_SPRING_BOOT4, true), configurator-> + configurator.addInlineSchemaOption("RESOLVE_INLINE_ENUMS", "false") + ); + JavaFileAssert.assertThat(files.get("CargoParent.java")) + .fileContains("public Object getUnit();"); + } } diff --git a/modules/openapi-generator/src/test/resources/3_0/spring/issue_19194_v2.yaml b/modules/openapi-generator/src/test/resources/3_0/spring/issue_19194_v2.yaml new file mode 100644 index 000000000000..5f1f69d91229 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/spring/issue_19194_v2.yaml @@ -0,0 +1,88 @@ +openapi: 3.0.3 +info: + title: test + contact: + email: support@company.de + license: + name: company Licence + url: https://company.de/ + version: 0.1.1 +servers: + - url: https://webservice.company.org +paths: + /doSomething: + post: + summary: test + description: description for test operation + operationId: doSomething + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/doSomethingRequest' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/doSomethingResponse' +components: + schemas: + doSomethingRequest: + title: doSomethingRequest + type: object + properties: + cargo: + $ref: '#/components/schemas/cargoParent' + required: + - cargo + doSomethingResponse: + title: doSomethingResponse + type: object + TonsUnitEnum: + type: string + enum: + - Tons + TONS: + title: TONS + type: object + properties: + unit: + $ref: '#/components/schemas/TonsUnitEnum' + amount: + type: number + format: double + example: 40 + minimum: 0 + required: + - unit + - amount + KILOGRAMS: + title: KILOGRAMS + type: object + properties: + unit: + type: string + pattern: ^KILOGRAMS$ + example: KILOGRAMS + amount: + type: number + format: double + example: 1000 + minimum: 0 + required: + - unit + - amount + cargoParent: + title: cargoParent + type: object + oneOf: + - $ref: '#/components/schemas/TONS' + - $ref: '#/components/schemas/KILOGRAMS' + discriminator: + propertyName: unit + mapping: + TONS: '#/components/schemas/TONS' + KILOGRAMS: '#/components/schemas/KILOGRAMS' \ No newline at end of file From 55a3b0618e826299792b20bb63bff49b0e0026fd Mon Sep 17 00:00:00 2001 From: jpfinne Date: Tue, 1 Sep 2026 22:19:16 +0200 Subject: [PATCH 16/26] Fix typo --- .../java/org/openapitools/codegen/utils/DiscriminatorUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 4a63c57c71fc..0123ca067b0b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -339,7 +339,7 @@ public static List getDistinctTypes(OpenAPI openAPI, Schema schema, Stri } /** - * return the list of deferenced mapping schemas. + * return the list of dereferenced mapping schemas. * @return the schemas found or empty list if not found. */ public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { From c17db5d23a9bf382fc71415df6d050f125b0c11e Mon Sep 17 00:00:00 2001 From: jpfinne Date: Wed, 2 Sep 2026 09:10:20 +0200 Subject: [PATCH 17/26] More tests --- .../openapitools/codegen/DefaultCodegen.java | 8 +- .../java/spring/SpringCodegenTest.java | 29 ++++--- .../test/resources/3_0/oneOf_issue_19194.yaml | 83 +++++++++++++++++++ ...9194_v2.yaml => oneOf_issue_19194_v2.yaml} | 0 4 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/oneOf_issue_19194.yaml rename modules/openapi-generator/src/test/resources/3_0/{spring/issue_19194_v2.yaml => oneOf_issue_19194_v2.yaml} (100%) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index a1fd259437da..6789eab903d3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3892,14 +3892,16 @@ protected String getCommonSchemaType(List schemas) { //. keep string for backward compatibility return typeMapping.get("string"); case 1: + Schema first = schemas.get(0); try { if (StringUtils.isEmpty(first.get$ref())) { - Schema refScheme = ModelUtils.getReferencedSchema(openAPI, first); - if (ModelUtils.isEnumSchema(refScheme)) { + if (ModelUtils.isEnumSchema(first)) { + // TODO: improve handling of inline enums)) { return toModelName(first.getName()); } - return getSchemaType(refScheme); + Schema refScheme = ModelUtils.getReferencedSchema(openAPI, first); + return getSingleSchemaType(refScheme); } return toModelName(ModelUtils.getSimpleRef(first.get$ref())); } catch (Exception e) { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index bd5f5274f340..840e37af8328 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9360,25 +9360,24 @@ public void issue_24769() throws IOException { .fileContains("public PetEnumType getEnumRefType();"); } - @Test - public void issue_19194() throws IOException { - Map files = generateFromContract( - "src/test/resources/3_0/oneOf_issue_19194.yaml", SPRING_BOOT, - Map.of(USE_SPRING_BOOT4, true), configurator-> - configurator.addInlineSchemaOption("RESOLVE_INLINE_ENUMS", "true") - ); - JavaFileAssert.assertThat(files.get("CargoInterface.java")) - .fileContains("public CargoGeneralParameterUnit getUnit();"); + @DataProvider(name = "oneOfDiscriminatorType") + public Object[][] oneOfDiscriminatorType() { + return new Object[][]{ + {"/3_0/oneOf_issue_19194.yaml", true, "CargoInterface.java", "public CargoGeneralParameterUnit getUnit();"}, +// {"/3_0/oneOf_issue_19194.yaml", false, "CargoInterface.java", "public CargoGeneralParameterUnitEnum getUnit();"}, + {"/3_0/oneOf_issue_19194_v2.yaml", false, "CargoParent.java", "public Object getUnit()"} + }; } - @Test - public void issue_19194_v2() throws IOException { + @Test(dataProvider = "oneOfDiscriminatorType") + public void oneOfDiscriminatorType(String filename, boolean resolveInlineEnum, String fileToCheck, String expectedContains) throws IOException { Map files = generateFromContract( - "src/test/resources/3_0/spring/issue_19194_v2.yaml", SPRING_BOOT, + "src/test/resources" + filename, SPRING_BOOT, Map.of(USE_SPRING_BOOT4, true), configurator-> - configurator.addInlineSchemaOption("RESOLVE_INLINE_ENUMS", "false") + configurator.addInlineSchemaOption("RESOLVE_INLINE_ENUMS", Boolean.toString(resolveInlineEnum)) ); - JavaFileAssert.assertThat(files.get("CargoParent.java")) - .fileContains("public Object getUnit();"); + JavaFileAssert.assertThat(files.get(fileToCheck)) + .fileContains(expectedContains); } + } diff --git a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_19194.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_19194.yaml new file mode 100644 index 000000000000..837312b10c40 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_19194.yaml @@ -0,0 +1,83 @@ +openapi: 3.0.3 +info: + title: test + contact: + email: support@company.de + license: + name: company Licence + url: https://company.de/ + version: 0.1.1 +servers: + - url: https://webservice.company.org +paths: + /doSomething: + post: + summary: test + description: description for test operation + operationId: doSomething + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/doSomethingRequest' + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/doSomethingResponse' +components: + schemas: + doSomethingRequest: + title: doSomethingRequest + type: object + properties: + cargo: + $ref: '#/components/schemas/cargoInterface' + required: + - cargo + doSomethingResponse: + title: doSomethingResponse + type: object + cargoInterface: + oneOf: + - $ref: '#/components/schemas/TONS' + - $ref: '#/components/schemas/KILOGRAMS' + discriminator: + propertyName: unit + TONS: + allOf: + - $ref: '#/components/schemas/cargoGeneralParameter' + - type: object + properties: + amount: + type: integer + required: + - amount + KILOGRAMS: + allOf: + - $ref: '#/components/schemas/cargoGeneralParameter' + - type: object + properties: + amount: + type: number + format: double + example: 1000 + minimum: 0 + required: + - amount + cargoGeneralParameter: + type: object + properties: + unit: + type: string + enum: + - KILOGRAMS + - TONS + cargoId: + type: integer + required: + - unit + - cargoId \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/3_0/spring/issue_19194_v2.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_19194_v2.yaml similarity index 100% rename from modules/openapi-generator/src/test/resources/3_0/spring/issue_19194_v2.yaml rename to modules/openapi-generator/src/test/resources/3_0/oneOf_issue_19194_v2.yaml From 27842a2d9f95e3e56802136ef24307ca1c347eb5 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Wed, 2 Sep 2026 17:40:50 +0200 Subject: [PATCH 18/26] Fix string not mapped --- .../openapitools/codegen/DefaultCodegen.java | 18 ++++++++---------- .../codegen/utils/DiscriminatorUtils.java | 19 ++++++++++++------- .../java/spring/SpringCodegenTest.java | 5 +++-- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 6789eab903d3..95b5ce35a793 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3895,24 +3895,22 @@ protected String getCommonSchemaType(List schemas) { Schema first = schemas.get(0); try { - if (StringUtils.isEmpty(first.get$ref())) { - if (ModelUtils.isEnumSchema(first)) { - // TODO: improve handling of inline enums)) { - return toModelName(first.getName()); - } - Schema refScheme = ModelUtils.getReferencedSchema(openAPI, first); - return getSingleSchemaType(refScheme); + if (StringUtils.isNotEmpty(first.get$ref())) { + return toModelName(ModelUtils.getSimpleRef(first.get$ref())); } - return toModelName(ModelUtils.getSimpleRef(first.get$ref())); } catch (Exception e) { // fallback for some unit test misconfigurations... - LOGGER.warn("Unable to getSchemaType for " + first, e); + LOGGER.warn("Unable to model name for " + first, e); return typeMapping.get("string"); } default: break; } schemas = schemas.stream().map(s -> ModelUtils.getReferencedSchema(openAPI, s)).collect(Collectors.toList()); + return getCommonTypeMapping(schemas); + } + + protected String getCommonTypeMapping(List schemas) { String simpleType = "object"; if (schemas.stream().allMatch(ModelUtils::isEnumSchema)) { simpleType = "enum"; @@ -3925,7 +3923,7 @@ protected String getCommonSchemaType(List schemas) { } else if (schemas.stream().allMatch(ModelUtils::isNumberSchema)) { simpleType = "number"; } else if (schemas.stream().allMatch(s -> ModelUtils.isStringSchema(s) && s.getFormat() == null && !ModelUtils.isEnumSchema(s))) { - return "string"; + simpleType = "string"; } String type = typeMapping.get(simpleType); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 0123ca067b0b..0b442b86490f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -346,9 +346,7 @@ public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { if (schema.getDiscriminator() != null) { if (schema.getDiscriminator().getMapping() != null) { return schema.getDiscriminator().getMapping().values().stream() - .filter(Objects::nonNull) - .map(ref -> ref.indexOf('/') >= 0 ? ModelUtils.getSimpleRef(ref) : ref) - .map(ref -> ModelUtils.getSchema(openAPI, ref)) + .map(ref -> getReferencedSchema(openAPI, ref)) .filter(Objects::nonNull) .collect(Collectors.toList()); } @@ -357,10 +355,7 @@ public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { List oneOfs = (List) schema.getOneOf(); return oneOfs.stream() .filter(Objects::nonNull) - .map(oneOf -> ((Schema)oneOf).get$ref()) - .filter(Objects::nonNull) - .map(ref -> ref.indexOf('/') >= 0 ? ModelUtils.getSimpleRef(ref) : ref) - .map(ref -> ModelUtils.getSchema(openAPI, (String)ref)) + .map(oneOf -> oneOf.get$ref()!=null? getReferencedSchema(openAPI, oneOf.get$ref()): oneOf) .filter(Objects::nonNull) .collect(Collectors.toList()); } @@ -368,6 +363,16 @@ public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { return Collections.emptyList(); } + private static Schema getReferencedSchema(OpenAPI openAPI, String ref) { + if (ref == null) { + return null; + } + if (ref.indexOf('/') >= 0) { + ref = ModelUtils.getSimpleRef(ref); + } + return ModelUtils.getSchema(openAPI, ref); + } + /** * Recursively try to find the schema matching the propertyName. * diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 840e37af8328..022e1cb339f7 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9364,8 +9364,9 @@ public void issue_24769() throws IOException { public Object[][] oneOfDiscriminatorType() { return new Object[][]{ {"/3_0/oneOf_issue_19194.yaml", true, "CargoInterface.java", "public CargoGeneralParameterUnit getUnit();"}, -// {"/3_0/oneOf_issue_19194.yaml", false, "CargoInterface.java", "public CargoGeneralParameterUnitEnum getUnit();"}, - {"/3_0/oneOf_issue_19194_v2.yaml", false, "CargoParent.java", "public Object getUnit()"} + {"/3_0/oneOf_issue_19194.yaml", false, "CargoInterface.java", "public Enum getUnit();"}, + {"/3_0/oneOf_issue_19194_v2.yaml", false, "CargoParent.java", "public Object getUnit()"}, + {"/3_0/oneof_polymorphism_and_inheritance.yaml", false, "FooRefOrValue.java", "public String getAtType()"} }; } From 9a284949d4b9da42f9c9e6ed9caaf598cea25cf3 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Thu, 3 Sep 2026 17:32:14 +0200 Subject: [PATCH 19/26] Use getPrimitiveType() --- .../openapitools/codegen/DefaultCodegen.java | 40 +++++++++---------- .../codegen/utils/DiscriminatorUtils.java | 4 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 95b5ce35a793..61a315f787e4 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3912,26 +3912,26 @@ protected String getCommonSchemaType(List schemas) { protected String getCommonTypeMapping(List schemas) { String simpleType = "object"; - if (schemas.stream().allMatch(ModelUtils::isEnumSchema)) { - simpleType = "enum"; - } else if (schemas.stream().allMatch(ModelUtils::isLongSchema)) { - simpleType = "long"; - } else if (schemas.stream().allMatch(ModelUtils::isShortSchema)) { - simpleType = "short"; - } else if (schemas.stream().allMatch(ModelUtils::isIntegerSchema)) { - simpleType = "integer"; - } else if (schemas.stream().allMatch(ModelUtils::isNumberSchema)) { - simpleType = "number"; - } else if (schemas.stream().allMatch(s -> ModelUtils.isStringSchema(s) && s.getFormat() == null && !ModelUtils.isEnumSchema(s))) { - simpleType = "string"; - } - - String type = typeMapping.get(simpleType); - if (type == null) { - // some typeMapping are not defined. Fallback to object. - return typeMapping.get("object"); - } - return type; + Set types = schemas.stream().map(this::getPrimitiveType).collect(Collectors.toSet()); + if (types.size() == 1) { + String commonType = types.iterator().next(); + if (typeMapping.containsKey(commonType)) { + // matching simple type + simpleType = commonType; + } + } + if ("objects".equals(simpleType) || "string".equals(simpleType)) { + // alternate more strict options + simpleType = "object"; + // Some generators define enum typeMapping + if (schemas.stream().allMatch(ModelUtils::isEnumSchema) && typeMapping.containsKey("enum")) { + simpleType = "enum"; + } else if (schemas.stream().allMatch(s -> ModelUtils.isStringSchema(s) && !ModelUtils.isEnumSchema(s))) { + // only string if there is no mix of enum and string + simpleType = "string"; + } + } + return typeMapping.get(simpleType); } /** diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 0b442b86490f..2968b5222f00 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -355,7 +355,9 @@ public static List getMappedSchemas(OpenAPI openAPI, Schema schema) { List oneOfs = (List) schema.getOneOf(); return oneOfs.stream() .filter(Objects::nonNull) - .map(oneOf -> oneOf.get$ref()!=null? getReferencedSchema(openAPI, oneOf.get$ref()): oneOf) + .map(oneOf -> oneOf.get$ref()!=null + ? getReferencedSchema(openAPI, oneOf.get$ref()) + : oneOf) .filter(Objects::nonNull) .collect(Collectors.toList()); } From 97909e8a3b1ed2a9654c5225d8ff8ed4c93f913b Mon Sep 17 00:00:00 2001 From: jpfinne Date: Thu, 3 Sep 2026 19:03:32 +0200 Subject: [PATCH 20/26] Enum improvement --- .../openapitools/codegen/DefaultCodegen.java | 45 ++++++++++++------- .../codegen/utils/DiscriminatorUtils.java | 2 + 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 61a315f787e4..c711eb8b8601 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3898,6 +3898,13 @@ protected String getCommonSchemaType(List schemas) { if (StringUtils.isNotEmpty(first.get$ref())) { return toModelName(ModelUtils.getSimpleRef(first.get$ref())); } + if (ModelUtils.isEnumSchema(first)) { + // inline enum, use the least common denominator + String simpleType = typeMapping.get("enum"); + + return simpleType != null? simpleType: typeMapping.get("object"); + } + return typeMapping.get(getPrimitiveType(first)); } catch (Exception e) { // fallback for some unit test misconfigurations... LOGGER.warn("Unable to model name for " + first, e); @@ -3910,25 +3917,31 @@ protected String getCommonSchemaType(List schemas) { return getCommonTypeMapping(schemas); } + /** + * Find the common type between different schemas. + * + * @param schemas list of more than one schema. + * @return the common matching mapped type + */ protected String getCommonTypeMapping(List schemas) { String simpleType = "object"; - Set types = schemas.stream().map(this::getPrimitiveType).collect(Collectors.toSet()); - if (types.size() == 1) { - String commonType = types.iterator().next(); - if (typeMapping.containsKey(commonType)) { - // matching simple type - simpleType = commonType; - } - } - if ("objects".equals(simpleType) || "string".equals(simpleType)) { - // alternate more strict options - simpleType = "object"; - // Some generators define enum typeMapping - if (schemas.stream().allMatch(ModelUtils::isEnumSchema) && typeMapping.containsKey("enum")) { + + boolean allEnums = schemas.stream().allMatch(ModelUtils::isEnumSchema); + if (allEnums) { + // non matching enums + if (typeMapping.containsKey("enum")) { simpleType = "enum"; - } else if (schemas.stream().allMatch(s -> ModelUtils.isStringSchema(s) && !ModelUtils.isEnumSchema(s))) { - // only string if there is no mix of enum and string - simpleType = "string"; + } + return typeMapping.get(simpleType); + } + if (schemas.stream().noneMatch(ModelUtils::isEnumSchema)) { + Set types = schemas.stream().map(this::getPrimitiveType).collect(Collectors.toSet()); + if (types.size() == 1) { + String foundPrimitiveType = types.iterator().next(); + if (typeMapping.containsKey(foundPrimitiveType)) { + // matching simple type + simpleType = foundPrimitiveType; + } } } return typeMapping.get(simpleType); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 2968b5222f00..1c9ea56ebd97 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -332,6 +332,8 @@ private static CodegenProperty getDiscriminatorCodegenProperty(OpenAPI openAPI, */ public static List getDistinctTypes(OpenAPI openAPI, Schema schema, String discPropName) { List mappedSchemas = getMappedSchemas(openAPI, schema); + + List properties = mappedSchemas.stream().map(sc -> findProperty(openAPI, sc, discPropName, new HashSet<>())).collect(Collectors.toList());; return mappedSchemas.stream().map(sc -> findProperty(openAPI, sc, discPropName, new HashSet<>())) .filter(Objects::nonNull) .distinct() From 74593abf6c8cc92b8d0618d343f65ab625b2fb44 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sat, 5 Sep 2026 10:37:37 +0200 Subject: [PATCH 21/26] Test more cases --- .../openapitools/codegen/DefaultCodegen.java | 13 +++- .../codegen/utils/DiscriminatorUtils.java | 58 +++++++++++++-- .../java/spring/SpringCodegenTest.java | 6 ++ .../test/resources/3_0/oneOf_issue_24769.yaml | 71 ++++++++++++++++++- 4 files changed, 139 insertions(+), 9 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index c711eb8b8601..7933cd2a92ff 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3913,6 +3913,17 @@ protected String getCommonSchemaType(List schemas) { default: break; } + +// boolean allRef = schemas.stream().allMatch(s -> s.get$ref() != null); +// if (allRef) { +// Set modelNames = schemas.stream() +// .map(s -> toModelName(ModelUtils.getSimpleRef(s.get$ref()))) +// .filter(Objects::nonNull) +// .collect(Collectors.toSet()); +// if (modelNames.size() == 1) { +// return modelNames.iterator().next(); +// } +// } schemas = schemas.stream().map(s -> ModelUtils.getReferencedSchema(openAPI, s)).collect(Collectors.toList()); return getCommonTypeMapping(schemas); } @@ -3928,7 +3939,7 @@ protected String getCommonTypeMapping(List schemas) { boolean allEnums = schemas.stream().allMatch(ModelUtils::isEnumSchema); if (allEnums) { - // non matching enums + // non matching enums. Use enum if if the langage can map it. if (typeMapping.containsKey("enum")) { simpleType = "enum"; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 1c9ea56ebd97..6e4d73bab54e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -333,11 +333,21 @@ private static CodegenProperty getDiscriminatorCodegenProperty(OpenAPI openAPI, public static List getDistinctTypes(OpenAPI openAPI, Schema schema, String discPropName) { List mappedSchemas = getMappedSchemas(openAPI, schema); - List properties = mappedSchemas.stream().map(sc -> findProperty(openAPI, sc, discPropName, new HashSet<>())).collect(Collectors.toList());; - return mappedSchemas.stream().map(sc -> findProperty(openAPI, sc, discPropName, new HashSet<>())) - .filter(Objects::nonNull) - .distinct() - .collect(Collectors.toList()); + + //List properties = mappedSchemas.stream().map(sc -> findProperty(openAPI, sc, discPropName, new HashSet<>())).collect(Collectors.toList());; + + List properties = new ArrayList<>(); + for (Schema s: mappedSchemas) { + Schema prop = findProperty(openAPI, s, discPropName, new HashSet<>()); + if (properties.stream().noneMatch(p -> + Objects.equals(p.getType(), prop.getType()) && + Objects.equals(p.get$ref(), prop.get$ref()) && + Objects.equals(p.getEnum(), prop.getEnum()) && + Objects.equals(p.getFormat(), prop.getFormat()))) { + properties.add(prop); + } + } + return properties; } /** @@ -392,21 +402,55 @@ public static Schema findProperty(OpenAPI openAPI, Schema schema, String propert if (properties != null) { Schema property = properties.get(propertyName); if (property != null) { - return property; + Schema found = simplifyProperty(property, visitedSchemas); + return found; } } + // loop into parent allOfs List allOfs = schema.getAllOf(); if (allOfs != null) { for (Schema child : allOfs) { Schema found = findProperty(openAPI, child, propertyName, visitedSchemas); if (found != null) { - return found; + return simplifyProperty(found, visitedSchemas); } } } return null; } + private static Schema simplifyProperty(Schema schema, Set visitedSchemas) { + if (!ModelUtils.isAllOf(schema)) { + return schema; + } + if (visitedSchemas.contains(schema)) { + return null; + } + visitedSchemas.add(schema); + + /* + * handle type+description. For Example: + * petType: + * $ref: '#/components/schemas/PetType' + * description: DOG + */ + Schema found = null; + int count = 0; + for (Schema sc: (List)schema.getAllOf()) { + if (ModelUtils.isAnyType(sc) || ModelUtils.hasRef(sc) ) { + found = sc; + } else { + count++; + } + } + if (count == schema.getAllOf().size()-1) { + return found; + } + // multiple types, too complex. + return null; + + } + public static class DiscriminatorData { private final Discriminator discriminator; diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 022e1cb339f7..78ec3f09b30e 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9358,6 +9358,12 @@ public void issue_24769() throws IOException { JavaFileAssert.assertThat(files.get("PetEnumRef.java")) .fileContains("public PetEnumType getEnumRefType();"); + + JavaFileAssert.assertThat(files.get("PetWithParent.java")) + .fileContains("public PetEnumType getPetType();"); + + JavaFileAssert.assertThat(files.get("PetNoMapping.java")) + .fileContains("public Enum getType();"); } @DataProvider(name = "oneOfDiscriminatorType") diff --git a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml index 3293d2c1c78c..43330d09150d 100644 --- a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml @@ -96,6 +96,7 @@ components: properties: enumRefType: $ref: '#/components/schemas/PetEnumType' + description: dog type packSize: type: integer format: int32 @@ -106,4 +107,72 @@ components: type: string enum: - CAT - - DOG \ No newline at end of file + - DOG + ParentType: + type: object + properties: + petType: + $ref: '#/components/schemas/PetEnumType' + PetWithParent: + oneOf: + - $ref: '#/components/schemas/CatWithParent' + - $ref: '#/components/schemas/DogWithParent' + discriminator: + propertyName: petType + mapping: + CAT: '#/components/schemas/CatWithParent' + DOG: '#/components/schemas/DogWithParent' + CatWithParent: + description: A representation of a cat + allOf: + - $ref: '#/components/schemas/ParentType' + - type: object + properties: + huntingSkill: + type: string + required: + - petType + - huntingSkill + DogWithParent: + description: A representation of a dog + allOf: + - $ref: '#/components/schemas/ParentType' + - type: object + properties: + packSize: + type: integer + format: int32 + required: + - petType + - packSize + PetNoMapping: + oneOf: + - $ref: '#/components/schemas/CatNoMapping' + - $ref: '#/components/schemas/DogNoMapping' + discriminator: + propertyName: type + CatNoMapping: + description: A representation of a cat + type: object + properties: + type: + type: string + const: CAT + huntingSkill: + type: string + required: + - type + - huntingSkill + DogNoMapping: + description: A representation of a dog + type: object + properties: + type: + type: string + const: DOG + packSize: + type: integer + format: int32 + required: + - type + - packSize \ No newline at end of file From 247125e329d20ec311a3caf691be83a42ca5728c Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sat, 5 Sep 2026 11:47:50 +0200 Subject: [PATCH 22/26] Avoid NPE --- .../org/openapitools/codegen/utils/DiscriminatorUtils.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index 6e4d73bab54e..cd610a350ee0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -332,14 +332,10 @@ private static CodegenProperty getDiscriminatorCodegenProperty(OpenAPI openAPI, */ public static List getDistinctTypes(OpenAPI openAPI, Schema schema, String discPropName) { List mappedSchemas = getMappedSchemas(openAPI, schema); - - - //List properties = mappedSchemas.stream().map(sc -> findProperty(openAPI, sc, discPropName, new HashSet<>())).collect(Collectors.toList());; - List properties = new ArrayList<>(); for (Schema s: mappedSchemas) { Schema prop = findProperty(openAPI, s, discPropName, new HashSet<>()); - if (properties.stream().noneMatch(p -> + if (prop != null && properties.stream().noneMatch(p -> Objects.equals(p.getType(), prop.getType()) && Objects.equals(p.get$ref(), prop.get$ref()) && Objects.equals(p.getEnum(), prop.getEnum()) && From 311dfcabf6fe43f4f306d61d3b8b912e81cc79e2 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sat, 5 Sep 2026 17:01:04 +0200 Subject: [PATCH 23/26] Improve allOf support --- .../codegen/utils/DiscriminatorUtils.java | 8 +++-- .../java/spring/SpringCodegenTest.java | 3 ++ .../test/resources/3_0/oneOf_issue_24769.yaml | 33 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java index cd610a350ee0..6ef83fe59f33 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java @@ -415,6 +415,9 @@ public static Schema findProperty(OpenAPI openAPI, Schema schema, String propert return null; } + /** + * simplify allOf property represented as [$ref, {description: ...}]. + */ private static Schema simplifyProperty(Schema schema, Set visitedSchemas) { if (!ModelUtils.isAllOf(schema)) { return schema; @@ -433,13 +436,12 @@ private static Schema simplifyProperty(Schema schema, Set visitedSchemas Schema found = null; int count = 0; for (Schema sc: (List)schema.getAllOf()) { - if (ModelUtils.isAnyType(sc) || ModelUtils.hasRef(sc) ) { + if (ModelUtils.getType(sc) != null || ModelUtils.hasRef(sc)) { found = sc; - } else { count++; } } - if (count == schema.getAllOf().size()-1) { + if (count == 1) { return found; } // multiple types, too complex. diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 78ec3f09b30e..b605987a03a8 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9364,6 +9364,9 @@ public void issue_24769() throws IOException { JavaFileAssert.assertThat(files.get("PetNoMapping.java")) .fileContains("public Enum getType();"); + + JavaFileAssert.assertThat(files.get("PetWithAllOf.java")) + .fileContains("public PetEnumType getTypeAllOf()"); } @DataProvider(name = "oneOfDiscriminatorType") diff --git a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml index 43330d09150d..e2754a950f60 100644 --- a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml @@ -145,6 +145,39 @@ components: required: - petType - packSize + PetWithAllOf: + oneOf: + - $ref: '#/components/schemas/CatWithAllOf' + - $ref: '#/components/schemas/DogWithAllOf' + discriminator: + propertyName: typeAllOf + CatWithAllOf: + description: A representation of a cat + type: object + properties: + typeAllOf: + allOf: + - $ref: '#/components/schemas/PetEnumType' + - deprecated: true + description: CAT + huntingSkill: + type: string + required: + - enumRefType + - huntingSkill + DogWithAllOf: + description: A representation of a dog + type: object + properties: + typeAllOf: + $ref: '#/components/schemas/PetEnumType' + description: dog type + packSize: + type: integer + format: int32 + required: + - enumRefType + - packSize PetNoMapping: oneOf: - $ref: '#/components/schemas/CatNoMapping' From 6f10cecc0af96f65fb320f2d18602aa3742710fc Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sat, 5 Sep 2026 17:14:03 +0200 Subject: [PATCH 24/26] Fix typo --- .../src/test/resources/3_0/oneOf_issue_24769.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml index e2754a950f60..55f80df5c419 100644 --- a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml @@ -163,20 +163,21 @@ components: huntingSkill: type: string required: - - enumRefType + - typeAllOf - huntingSkill DogWithAllOf: description: A representation of a dog type: object properties: typeAllOf: - $ref: '#/components/schemas/PetEnumType' - description: dog type + allOf: + - $ref: '#/components/schemas/PetEnumType' + description: dog type packSize: type: integer format: int32 required: - - enumRefType + - typeAllOf - packSize PetNoMapping: oneOf: From 6d1e85301ccc1277315978e91de6479c5c942560 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sat, 5 Sep 2026 17:33:39 +0200 Subject: [PATCH 25/26] Add example with discriminator being enums --- .../java/spring/SpringCodegenTest.java | 12 +++++-- .../test/resources/3_0/oneOf_issue_24769.yaml | 36 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index b605987a03a8..0bd582e30076 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9362,11 +9362,19 @@ public void issue_24769() throws IOException { JavaFileAssert.assertThat(files.get("PetWithParent.java")) .fileContains("public PetEnumType getPetType();"); + JavaFileAssert.assertThat(files.get("PetWithAllOf.java")) + .fileContains("public PetEnumType getTypeAllOf()"); + + JavaFileAssert.assertThat(files.get("PetWithEnum.java")) + .fileContains("public Enum getEnumType()"); + JavaFileAssert.assertThat(files.get("CatWithEnum.java")) + .fileContains("public enum EnumTypeEnum {", + "CAT(\"CAT\");"); + + JavaFileAssert.assertThat(files.get("PetNoMapping.java")) .fileContains("public Enum getType();"); - JavaFileAssert.assertThat(files.get("PetWithAllOf.java")) - .fileContains("public PetEnumType getTypeAllOf()"); } @DataProvider(name = "oneOfDiscriminatorType") diff --git a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml index 55f80df5c419..95368dd6e82b 100644 --- a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml @@ -165,6 +165,42 @@ components: required: - typeAllOf - huntingSkill + PetWithEnum: + oneOf: + - $ref: '#/components/schemas/CatWithEnum' + - $ref: '#/components/schemas/DogWithEnum' + discriminator: + propertyName: enumType + mapping: + CAT: '#/components/schemas/CatWithEnum' + DOG: '#/components/schemas/DogWithEnum' + CatWithEnum: + description: A representation of a cat + type: object + properties: + enumType: + type: string + enum: + - CAT + huntingSkill: + type: string + required: + - type + - huntingSkill + DogWithEnum: + description: A representation of a dog + type: object + properties: + enumType: + type: string + enum: + - DOG + packSize: + type: integer + format: int32 + required: + - type + - packSize DogWithAllOf: description: A representation of a dog type: object From 93051c94d1861b212a4e8ec2f595dde237ed5fe1 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Sat, 5 Sep 2026 18:04:42 +0200 Subject: [PATCH 26/26] Fix required enumType --- .../src/test/resources/3_0/oneOf_issue_24769.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml index 95368dd6e82b..131faa030a6b 100644 --- a/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/oneOf_issue_24769.yaml @@ -185,7 +185,7 @@ components: huntingSkill: type: string required: - - type + - enumType - huntingSkill DogWithEnum: description: A representation of a dog @@ -199,7 +199,7 @@ components: type: integer format: int32 required: - - type + - enumType - packSize DogWithAllOf: description: A representation of a dog