Skip to content

[Java][Spring] useRecords: records with additionalProperties - #25387

Merged
wing328 merged 1 commit into
OpenAPITools:masterfrom
Sekator778:spring-records-step5
Oct 31, 2026
Merged

wing328 merged 1 commit into
OpenAPITools:masterfrom
Sekator778:spring-records-step5

Conversation

@Sekator778

@Sekator778 Sekator778 commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Fifth step towards #10490. With useRecords and useSpringBoot4, a model with additionalProperties becomes a record whose last component is the map, under @JsonAnySetter and @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

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    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.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

With useRecords on Spring Boot 4, a model with additionalProperties is now generated as a record whose map of undeclared properties is its last component, annotated with @JsonAnySetter and @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.

  • The compact constructor gives an absent map an empty LinkedHashMap, and getAdditionalProperty(key) accesses entries.
  • Builders accept and copy the map; toBuilder keeps the map's entries.
  • Models bound from request parameters and models with a property named additionalProperties also stay classes.
  • Adds the spring-boot-4-records sample 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.

View guided diff

… 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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Suggested change
<springdoc.version>2.6.0</springdoc.version>
<springdoc.version>3.0.3</springdoc.version>

id:
type: string
additionalProperties:
type: array

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@Sekator778

Copy link
Copy Markdown
Contributor Author

This is step 5 of the Spring records line towards #10490: steps 1 to 4 are merged (#25200, #25238, #25273, #25351), and this one adds additionalProperties. allOf is next, then the last PR that closes #10490. It would be good to have it in 7.27.0 on 02.11.

@wing328
wing328 merged commit eef27d9 into OpenAPITools:master Oct 31, 2026
74 checks passed
@wing328 wing328 added this to the 7.27.0 milestone Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants