Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
29c6207
fix: normalize OAS 3.1 schemas with type:[object,"null"] to set nulla…
Picazsoo Jun 26, 2026
cb5aa49
fix: ensure OAS 3.1 schemas with type array including "null" set null…
Picazsoo Jun 26, 2026
b0d188d
Revert "fix: ensure OAS 3.1 schemas with type array including "null" …
Picazsoo Jun 26, 2026
74eb333
Reapply "fix: ensure OAS 3.1 schemas with type array including "null"…
Picazsoo Jun 26, 2026
6a4bd10
add whitespace to retrigger tests
Picazsoo Jun 27, 2026
68e6dff
Revert "add whitespace to retrigger tests"
Picazsoo Jun 27, 2026
7f0b9a6
Revert "Reapply "fix: ensure OAS 3.1 schemas with type array includin…
Picazsoo Jun 27, 2026
500d93a
Reapply "fix: ensure OAS 3.1 schemas with type array including "null"…
Picazsoo Jun 27, 2026
cf56cf5
Revert "fix: ensure OAS 3.1 schemas with type array including "null" …
Picazsoo Jun 27, 2026
f741e48
Revert "fix: normalize OAS 3.1 schemas with type:[object,"null"] to s…
Picazsoo Jun 27, 2026
39ae5a2
Reapply "fix: normalize OAS 3.1 schemas with type:[object,"null"] to …
Picazsoo Jun 27, 2026
fbd542d
Reapply "fix: ensure OAS 3.1 schemas with type array including "null"…
Picazsoo Jun 27, 2026
080e3e2
Revert "Reapply "fix: ensure OAS 3.1 schemas with type array includin…
Picazsoo Jun 27, 2026
de3e753
Reapply "Reapply "fix: ensure OAS 3.1 schemas with type array includi…
Picazsoo Jun 27, 2026
a275fa0
Reapply "add whitespace to retrigger tests"
Picazsoo Jun 27, 2026
7eca935
ci: replace setup-cpp with apt-get in node2 to fix transient GPG key …
Picazsoo Jun 28, 2026
c327dfc
fix: add explicit source directory '.' to cmake invocation in cpp-res…
Picazsoo Jun 28, 2026
c2169e6
fix: force clang compiler in cpp-restsdk cmake invocation
Picazsoo Jun 28, 2026
b7807bd
ci: remove unnecessary ninja-build from node2 apt install
Picazsoo Jun 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,18 @@ public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {

return schema;
} else if (ModelUtils.hasProperties(schema)) {
// OAS 3.1: if the type array includes "null", extract it and set nullable:true
// on the parent schema before normalizing its child properties.
// We intentionally do NOT call the full processNormalize31Spec here because
// that method can replace a JsonSchema with properties (but no explicit type)
// with an empty schema, discarding all properties.
if (getRule(NORMALIZE_31SPEC) && schema.getTypes() != null && schema.getTypes().contains("null")) {
schema.setNullable(true);
schema.getTypes().remove("null");
if (schema.getTypes().size() == 1) {
schema.setType(String.valueOf(schema.getTypes().iterator().next()));
}
}
normalizeProperties(schema, visitedSchemas);
} else if (schema.getAdditionalProperties() instanceof Schema) { // map
normalizeMapSchema(schema);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1896,4 +1896,62 @@ public void testLooseNullDefinitions() {
ModelUtils.looseNullDefinitions = false;
}

/**
* Verify that a schema defined as type:[object,"null"] WITH properties (OAS 3.1 style)
* is correctly normalized so that nullable:true is set on the schema itself.
* Regression test for https://github.com/OpenAPITools/openapi-generator/issues/24139
*/
@Test
public void testIssue24139NullableObjectWithPropertiesGetsNullableTrue() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/issue_24139.yaml");

// Before normalization: NestedNullable has types=[object,null], nullable is not yet set
Schema<?> nestedNullableBefore = openAPI.getComponents().getSchemas().get("NestedNullable");
assertNotNull(nestedNullableBefore);
assertNotNull(nestedNullableBefore.getProperties());

Map<String, String> options = new HashMap<>();
options.put("NORMALIZE_31SPEC", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

// After normalization: nullable must be true and type must be "object"
Schema<?> nestedNullableAfter = openAPI.getComponents().getSchemas().get("NestedNullable");
assertNotNull(nestedNullableAfter);
assertEquals(nestedNullableAfter.getNullable(), Boolean.TRUE,
"NestedNullable with type:[object,\"null\"] should have nullable:true after normalization");
assertEquals(nestedNullableAfter.getType(), "object");
assertNotNull(nestedNullableAfter.getProperties(),
"NestedNullable properties must be preserved after normalization");
}

/**
* Regression test: an OAS 3.1 schema with properties but NO explicit type declaration
* must keep its properties after NORMALIZE_31SPEC normalization.
* Regression for a potential regression introduced by the fix for issue 24139.
*/
@Test
public void testIssue24139ImpliedObjectSchemaKeepsProperties() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/issue_24139.yaml");

Schema<?> impliedBefore = openAPI.getComponents().getSchemas().get("ImpliedObject");
assertNotNull(impliedBefore);
assertNotNull(impliedBefore.getProperties());

Map<String, String> options = new HashMap<>();
options.put("NORMALIZE_31SPEC", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

Schema<?> impliedAfter = openAPI.getComponents().getSchemas().get("ImpliedObject");
assertNotNull(impliedAfter);
assertNotNull(impliedAfter.getProperties(),
"ImpliedObject (no explicit type, just properties) must keep its properties after normalization");
assertNotNull(impliedAfter.getProperties().get("name"),
"ImpliedObject.name property must be preserved after normalization");
assertNull(impliedAfter.getNullable(),
"ImpliedObject must not be marked nullable (no null type was declared)");
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -6813,4 +6813,35 @@ public void paramJsonPropertyAnnotationWithDigitStartingPropertyName() throws IO
"@param:JsonProperty(\"2nd_field\")\n @get:JsonProperty(\"2nd_field\") val `2ndField`"
);
}

Comment thread
Picazsoo marked this conversation as resolved.
/**
* Regression test for https://github.com/OpenAPITools/openapi-generator/issues/24139
* A property that $ref's an OAS 3.1 schema with type:[object,"null"] is nullable and must
* NOT receive @field:JsonSetter(nulls = Nulls.FAIL).
*/
@Test(description = "issue 24139: nullable $ref (type:[object,null]) must not get @JsonSetter(nulls = Nulls.FAIL)")
public void testIssue24139NullableRefNoJsonSetterNullsFail() throws IOException {
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put("useBeanValidation", true);
additionalProperties.put("openApiNullable", "true");

Map<String, File> files = generateFromContract(
"src/test/resources/3_1/issue_24139.yaml",
additionalProperties
);

File itemFile = files.get("Item.kt");
assertThat(itemFile).isNotNull();

// nestedNullable: $ref to NestedNullable (type:[object,"null"]) — nullable, no @JsonSetter(nulls = Nulls.FAIL)
assertFileNotContains(itemFile.toPath(), "nestedNullable: NestedNullable");
// The field must NOT have @JsonSetter(nulls = Nulls.FAIL) because the referenced schema is nullable
String content = org.apache.commons.io.FileUtils.readFileToString(itemFile, StandardCharsets.UTF_8);
// Extract the nestedNullable field block and verify annotation absence
Assert.assertFalse(
content.contains("@field:JsonSetter(nulls = Nulls.FAIL)\n @param:JsonProperty(\"nestedNullable\")") ||
content.contains("@field:JsonSetter(nulls = Nulls.FAIL)\n @get:JsonProperty(\"nestedNullable\")"),
"nestedNullable ($ref to nullable schema) must not have @JsonSetter(nulls = Nulls.FAIL)"
);
}
}
37 changes: 37 additions & 0 deletions modules/openapi-generator/src/test/resources/3_1/issue_24139.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
openapi: 3.1.1
info:
title: Test issue 24139
version: 1.0.0

components:
schemas:
NestedNullable:
type: [ object, "null" ]
properties:
value:
type: [ number, "null" ]
Nested:
type: object
properties:
value:
type: [ number, "null" ]
# OAS 3.1 allows object schemas with properties but no explicit type declaration.
# This must not lose its properties after normalization.
ImpliedObject:
properties:
name:
type: string

Item:
type: object
properties:
nestedNullable:
$ref: '#/components/schemas/NestedNullable'

nestedNullable2:
anyOf:
- $ref: '#/components/schemas/Nested'
- type: 'null'

nested:
$ref: '#/components/schemas/Nested'
Loading