Repository navigation
[Java][Spring] useRecords: records with additionalProperties - #25387
Conversation
… on Spring Boot 4 A model with additionalProperties becomes a record on Spring Boot 4: the undeclared properties are received by a Map component annotated with JsonAnySetter and JsonAnyGetter, which Jackson 2.18 and later (Spring Boot 4 brings 2.20 and 3.0) read through the constructor. On Spring Boot 3 the generated project brings Jackson 2.17, which would drop them, so the model stays a class and the reason is logged. A model bound from request parameters, or with a property named additionalProperties, stays a class too. The compact constructor gives an absent map the empty value, the record keeps getAdditionalProperty(key), and the builder takes the map.
There was a problem hiding this comment.
3 issues found across 33 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="modules/openapi-generator/src/test/resources/3_0/spring/records-additional-properties.yaml">
<violation number="1" location="modules/openapi-generator/src/test/resources/3_0/spring/records-additional-properties.yaml:56">
P2: This schema specifies integer-valued arrays, but the new record coverage accepts `Map<String, List>` and therefore hides the lost item type. Preserve the array generic (`Map<String, List<Integer>>`) or change this fixture’s assertion so the regression cannot ship unnoticed.</violation>
</file>
<file name="samples/openapi3/server/petstore/spring-boot-4-records/src/main/java/org/openapitools/model/Child.java">
<violation number="1" location="samples/openapi3/server/petstore/spring-boot-4-records/src/main/java/org/openapitools/model/Child.java:30">
P1: `Child` drops the additional properties declared by its schema. Add the class-style `Map<String, String>` catch-all with `@JsonAnySetter`/`@JsonAnyGetter`, and include it in equality, builders, and `toBuilder`, so inherited models retain the same additional-properties behavior as other generated classes.</violation>
</file>
<file name="samples/openapi3/server/petstore/spring-boot-4-records/pom.xml">
<violation number="1" location="samples/openapi3/server/petstore/spring-boot-4-records/pom.xml:12">
P1: This Boot 4 sample pins springdoc 2.6.0, but springdoc's compatibility matrix lists 3.0.x—not 2.x—for Spring Boot 4.0.x. Use a 3.0.x release so the generated sample builds against Boot 4.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| @Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.27.0-SNAPSHOT") | ||
| public class Child extends Base { | ||
|
|
||
| private @Nullable String x; |
There was a problem hiding this comment.
P1: Child drops the additional properties declared by its schema. Add the class-style Map<String, String> catch-all with @JsonAnySetter/@JsonAnyGetter, and include it in equality, builders, and toBuilder, so inherited models retain the same additional-properties behavior as other generated classes.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/openapi3/server/petstore/spring-boot-4-records/src/main/java/org/openapitools/model/Child.java, line 30:
<comment>`Child` drops the additional properties declared by its schema. Add the class-style `Map<String, String>` catch-all with `@JsonAnySetter`/`@JsonAnyGetter`, and include it in equality, builders, and `toBuilder`, so inherited models retain the same additional-properties behavior as other generated classes.</comment>
<file context>
@@ -0,0 +1,172 @@
+@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.27.0-SNAPSHOT")
+public class Child extends Base {
+
+ private @Nullable String x;
+
+ public Child() {
</file context>
| <java.version>17</java.version> | ||
| <maven.compiler.release>${java.version}</maven.compiler.release> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| <springdoc.version>2.6.0</springdoc.version> |
There was a problem hiding this comment.
P1: This Boot 4 sample pins springdoc 2.6.0, but springdoc's compatibility matrix lists 3.0.x—not 2.x—for Spring Boot 4.0.x. Use a 3.0.x release so the generated sample builds against Boot 4.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/openapi3/server/petstore/spring-boot-4-records/pom.xml, line 12:
<comment>This Boot 4 sample pins springdoc 2.6.0, but springdoc's compatibility matrix lists 3.0.x—not 2.x—for Spring Boot 4.0.x. Use a 3.0.x release so the generated sample builds against Boot 4.</comment>
<file context>
@@ -0,0 +1,73 @@
+ <java.version>17</java.version>
+ <maven.compiler.release>${java.version}</maven.compiler.release>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <springdoc.version>2.6.0</springdoc.version>
+ <swagger-ui.version>5.17.14</swagger-ui.version>
+ </properties>
</file context>
| <springdoc.version>2.6.0</springdoc.version> | |
| <springdoc.version>3.0.3</springdoc.version> |
| id: | ||
| type: string | ||
| additionalProperties: | ||
| type: array |
There was a problem hiding this comment.
P2: This schema specifies integer-valued arrays, but the new record coverage accepts Map<String, List> and therefore hides the lost item type. Preserve the array generic (Map<String, List<Integer>>) or change this fixture’s assertion so the regression cannot ship unnoticed.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modules/openapi-generator/src/test/resources/3_0/spring/records-additional-properties.yaml, line 56:
<comment>This schema specifies integer-valued arrays, but the new record coverage accepts `Map<String, List>` and therefore hides the lost item type. Preserve the array generic (`Map<String, List<Integer>>`) or change this fixture’s assertion so the regression cannot ship unnoticed.</comment>
<file context>
@@ -0,0 +1,87 @@
+ id:
+ type: string
+ additionalProperties:
+ type: array
+ items:
+ type: integer
</file context>
Fifth step towards #10490. With
useRecordsanduseSpringBoot4, a model with additionalProperties becomes a record whose last component is the map, under@JsonAnySetterand@JsonAnyGetter. Jackson reads such a component only from 2.18, and Spring Boot 3 brings 2.17, so on Boot 3 these models stay classes and the reason is logged. Unlike the class, the map keeps keys in reverse document order.@cachescrubber @welshm @MelleD @atextor @manedev79 @javisst @borsch @banlevente @Zomzog @martin-mfg @KannaKim
PR checklist
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example
./bin/generate-samples.sh bin/configs/java*.IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
Summary by cubic
With
useRecordson Spring Boot 4, a model withadditionalPropertiesis now generated as a record whose map of undeclared properties is its last component, annotated with@JsonAnySetterand@JsonAnyGetter. On Spring Boot 3 the model stays a class and the reason is logged, because reading the map through a record constructor requires Jackson 2.18 and the generated Boot 3 project brings 2.17.LinkedHashMap, andgetAdditionalProperty(key)accesses entries.toBuilderkeeps the map's entries.additionalPropertiesalso stay classes.spring-boot-4-recordssample and its tests.A model generated this way keeps map keys in reverse document order, unlike the class.
Written for commit 2ee2c45. Summary will update on new commits.