Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions bin/configs/spring-boot-jspecify.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
generatorName: spring
outputDir: samples/openapi3/server/petstore/springboot-jspecify
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
additionalProperties:
groupId: org.openapitools.openapi.jspecify
documentationProvider: springdoc
artifactId: springboot
snapshotVersion: "true"
useSpringBoot3: true
useBeanValidation: true
hideGenerationTimestamp: "true"
generateConstructorWithAllArgs: true
additionalModelTypeAnnotations: '@org.jspecify.annotations.NullMarked'
importMappings:
Nullable: org.jspecify.annotations.Nullable
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
Expand Down Expand Up @@ -1147,17 +1149,63 @@ private Set<String> reformatProvideArgsParams(Operation operation) {
return provideArgsClassSet;
}

@Data
@RequiredArgsConstructor
static class ConstructorSpecs {

static final String NULL_UNMARKED = "@org.jspecify.annotations.NullUnmarked";
static final String JSON_CREATOR = "@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)";
private final String constructorAnnotation;
private final boolean useJsonProperty;

static ConstructorSpecs useJsonProperty(boolean useJsonProperty) {
return new ConstructorSpecs(useJsonProperty?JSON_CREATOR: null, useJsonProperty);
}
}

/**
* Is JSpecify used?
*
* TODO: make it configurable
*/
public boolean isUseJspecify() {
return importMapping.getOrDefault("Nullable", "")
.contains("jspecify");
}

@Override
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
objs = super.postProcessAllModels(objs);

Map<String, CodegenModel> allModels = getAllModels(objs);
// conditionally force the generation of no args constructor

for (CodegenModel cm : allModels.values()) {
boolean hasLombokNoArgsConstructor = lombokAnnotations != null && lombokAnnotations.containsKey("NoArgsConstructor");
boolean hasAllArgsConstructor = cm.vendorExtensions.containsKey("x-java-all-args-constructor");

if (!hasLombokNoArgsConstructor
&& (cm.hasRequired || cm.vendorExtensions.containsKey("x-java-all-args-constructor"))) {
cm.vendorExtensions.put("x-java-no-args-constructor", true);
&& (cm.hasRequired || hasAllArgsConstructor)) {

String annotation = (cm.hasRequired && isUseJspecify())? ConstructorSpecs.NULL_UNMARKED: null;

ConstructorSpecs noArgSpec = new ConstructorSpecs(annotation, false);
cm.vendorExtensions.put("x-java-no-args-constructor", noArgSpec);
}

// improve the generation of required and all arguments constructor
boolean jsonPropertyAllowed = !this.withXml && jackson;
boolean requiredJsonProperty = false;
if (hasAllArgsConstructor) {
// add @JsonCreator and @JsonProperty on the all arguments constructor
cm.vendorExtensions.put("x-java-all-args-constructor", ConstructorSpecs.useJsonProperty(jsonPropertyAllowed));
} else if (this.generatedConstructorWithRequiredArgs && cm.hasRequired && cm.requiredVars.size() == cm.allVars.size()) {
/* add @JsonCreator and @JsonProperty on the required argument constructor
*/
requiredJsonProperty = jsonPropertyAllowed;
}
if (generatedConstructorWithRequiredArgs && cm.hasRequired) {
cm.vendorExtensions.put("x-java-required-args-constructor", ConstructorSpecs.useJsonProperty(requiredJsonProperty));
}
}
return objs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
{{#useJspecify}}
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>1.0.0</version>
</dependency>
{{/useJspecify}}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,19 +90,24 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
{{/vars}}
{{#vendorExtensions.x-java-no-args-constructor}}

{{#constructorAnnotation}}
{{{.}}}
{{/constructorAnnotation}}
public {{classname}}() {
super();
}
{{/vendorExtensions.x-java-no-args-constructor}}
{{^lombok.Data}}
{{^lombok.RequiredArgsConstructor}}
{{#generatedConstructorWithRequiredArgs}}
{{#hasRequired}}
{{#vendorExtensions.x-java-required-args-constructor}}

/**
* Constructor with only required parameters{{#generateConstructorWithAllArgs}}{{^vendorExtensions.x-java-all-args-constructor}} and all parameters{{/vendorExtensions.x-java-all-args-constructor}}{{/generateConstructorWithAllArgs}}
*/
public {{classname}}({{#requiredVars}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/requiredVars}}) {
{{#constructorAnnotation}}
{{{.}}}
{{/constructorAnnotation}}
public {{classname}}({{#requiredVars}}{{#useJsonProperty}}@JsonProperty("{{baseName}}") {{/useJsonProperty}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/requiredVars}}) {
{{#parent}}
super({{#parentRequiredVars}}{{name}}{{^-last}}, {{/-last}}{{/parentRequiredVars}});
{{/parent}}
Expand All @@ -117,15 +122,17 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
{{/required}}
{{/vars}}
}
{{/hasRequired}}
{{/generatedConstructorWithRequiredArgs}}
{{/vendorExtensions.x-java-required-args-constructor}}
{{/lombok.RequiredArgsConstructor}}
{{#vendorExtensions.x-java-all-args-constructor}}

/**
* Constructor with all args parameters
*/
public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{>nullableAnnotation}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) {
{{#constructorAnnotation}}
{{{.}}}
{{/constructorAnnotation}}
public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{#useJsonProperty}}@JsonProperty("{{baseName}}") {{/useJsonProperty}}{{>nullableAnnotation}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) {
{{#parent}}
super({{#parentVars}}{{name}}{{^-last}}, {{/-last}}{{/parentVars}});
{{/parent}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4833,6 +4833,8 @@ public void testAllArgsConstructor_16797_REFACTOR_ALLOF_WITH_PROPERTIES_ONLY() t
.hasParameter("requestId").toConstructor()
.hasParameter("success").toConstructor()
.hasParameter("pageInfo")
.toConstructor()
.assertConstructorAnnotations().containsWithName("JsonCreator");
;
}

Expand Down Expand Up @@ -4895,28 +4897,37 @@ public void testAllArgsConstructor_defaultOrder_15796() throws IOException {
.hasParameter("name").toConstructor()
.hasParameter("type").toConstructor()
.hasParameter("hairType").toConstructor()
.assertConstructorAnnotations().containsWithName("JsonCreator");
;
}

@Test
public void generateAllArgsConstructor() throws IOException {
Map<String, File> files = generateFromContract("src/test/resources/3_0/java/all_args_constructor.yaml", null,
Map.of(AbstractJavaCodegen.GENERATE_CONSTRUCTOR_WITH_ALL_ARGS, Boolean.TRUE, INTERFACE_ONLY, "true"),
codegenConfig -> codegenConfig.addOpenapiNormalizer("REFACTOR_ALLOF_WITH_PROPERTIES_ONLY", " true"));
codegenConfig -> codegenConfig.addOpenapiNormalizer("REFACTOR_ALLOF_WITH_PROPERTIES_ONLY", " true")
.setImportMappings(Map.of("Nullable", "org.jspecify.annotations.Nullable")));
JavaFileAssert.assertThat(files.get("Pet.java"))
.assertConstructor("String")
.hasParameter("type").toConstructor()
.toFileAssert()
.assertConstructor("LocalDate", "String", "String")
.hasParameter("dateOfBirth").toConstructor()
.hasParameter("name").toConstructor()
.hasParameter("type").toConstructor();
.hasParameter("type").toConstructor()
.assertConstructorAnnotations().containsWithName("JsonCreator").toConstructor()
.toFileAssert()
.assertConstructor("String")
.assertConstructorAnnotations().doesNotContainWithName("JsonCreator").toConstructor();
JavaFileAssert.assertThat(files.get("Cat.java"))
.assertConstructor("Integer", "String", "LocalDate", "String", "String");

// test required constructor
JavaFileAssert.assertThat(files.get("Page.java"))
.assertConstructor("Integer")
.assertConstructorAnnotations().containsWithName("JsonCreator")
.toConstructor()
.assertConstructorAnnotations().containsWithName("JsonCreator").toConstructor()
.toFileAssert()
.fileContains("Constructor with only required parameters and all parameters");

Expand Down Expand Up @@ -5353,14 +5364,14 @@ public void shouldAnnotateNonRequiredFieldsAsNullable() throws IOException {
JavaFileAssert.assertThat(file)
.fileContains(
"public Item(" +
"String mandatoryName," +
" @Nullable String optionalDescription," +
" String optionalOneWithDefault," +
" String nullableStr," +
" List<String> mandatoryContainer," +
" List<String> optionalContainer," +
" List<String> optionalContainerWithDefault," +
" List<String> nullableContainer)"
"@JsonProperty(\"mandatoryName\") String mandatoryName," +
" @JsonProperty(\"optionalDescription\") @Nullable String optionalDescription," +
" @JsonProperty(\"optionalOneWithDefault\") String optionalOneWithDefault," +
" @JsonProperty(\"nullableStr\") String nullableStr," +
" @JsonProperty(\"mandatoryContainer\") List<String> mandatoryContainer," +
" @JsonProperty(\"optionalContainer\") List<String> optionalContainer," +
" @JsonProperty(\"optionalContainerWithDefault\") List<String> optionalContainerWithDefault," +
" @JsonProperty(\"nullableContainer\") List<String> nullableContainer)"
);
}

Expand Down Expand Up @@ -5388,10 +5399,10 @@ public void shouldAnnotateNonRequiredFieldsAsNullableWhenSetContainerDefaultToNu
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.fileContains(
", List<String> mandatoryContainer," +
" @Nullable List<String> optionalContainer," +
" List<String> optionalContainerWithDefault," +
" List<String> nullableContainer)"
", @JsonProperty(\"mandatoryContainer\") List<String> mandatoryContainer," +
" @JsonProperty(\"optionalContainer\") @Nullable List<String> optionalContainer," +
" @JsonProperty(\"optionalContainerWithDefault\") List<String> optionalContainerWithDefault," +
" @JsonProperty(\"nullableContainer\") List<String> nullableContainer)"
);
}

Expand Down Expand Up @@ -5419,8 +5430,8 @@ public void shouldNotAnnotateNonRequiredFieldsAsNullableWhileUseOptional() throw
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.fileContains(
"public Item(String mandatoryName, String optionalDescription," +
" String optionalOneWithDefault, String nullableStr"
"public Item(@JsonProperty(\"mandatoryName\") String mandatoryName, @JsonProperty(\"optionalDescription\") String optionalDescription," +
" @JsonProperty(\"optionalOneWithDefault\") String optionalOneWithDefault, @JsonProperty(\"nullableStr\") String nullableStr"
);
}

Expand Down Expand Up @@ -5462,10 +5473,10 @@ public void shouldAnnotateNonRequiredFieldsAsNullableWhileNotUsingOpenApiNullabl

JavaFileAssert.assertThat(file)
.fileContains(
" List<String> mandatoryContainer," +
" @Nullable List<String> optionalContainer," +
" List<String> optionalContainerWithDefault," +
" @Nullable List<String> nullableContainer)"
" @JsonProperty(\"mandatoryContainer\") List<String> mandatoryContainer," +
" @JsonProperty(\"optionalContainer\") @Nullable List<String> optionalContainer," +
" @JsonProperty(\"optionalContainerWithDefault\") List<String> optionalContainerWithDefault," +
" @JsonProperty(\"nullableContainer\") @Nullable List<String> nullableContainer)"
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public TypeHolderDefault() {
/**
* Constructor with only required parameters
*/
public TypeHolderDefault(String stringItem, BigDecimal numberItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public TypeHolderDefault(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
this.stringItem = stringItem;
this.numberItem = numberItem;
this.integerItem = integerItem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public TypeHolderExample() {
/**
* Constructor with only required parameters
*/
public TypeHolderExample(String stringItem, BigDecimal numberItem, Float floatItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public TypeHolderExample(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("float_item") Float floatItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
this.stringItem = stringItem;
this.numberItem = numberItem;
this.floatItem = floatItem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public TypeHolderDefault() {
/**
* Constructor with only required parameters
*/
public TypeHolderDefault(String stringItem, BigDecimal numberItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public TypeHolderDefault(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
this.stringItem = stringItem;
this.numberItem = numberItem;
this.integerItem = integerItem;
Comment on lines +46 to 49
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

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

P2: @JsonCreator property-based constructor will receive null for missing JSON properties, so this change overwrites default field initializers with nulls and can violate @NotNull/default-value expectations.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/model/TypeHolderDefault.java, line 46:

<comment>@JsonCreator property-based constructor will receive null for missing JSON properties, so this change overwrites default field initializers with nulls and can violate @NotNull/default-value expectations.</comment>

<file context>
@@ -42,7 +42,8 @@ public TypeHolderDefault() {
    */
-  public TypeHolderDefault(String stringItem, BigDecimal numberItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
+  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+  public TypeHolderDefault(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
     this.stringItem = stringItem;
     this.numberItem = numberItem;
</file context>
Fix with Cubic

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public TypeHolderExample() {
/**
* Constructor with only required parameters
*/
public TypeHolderExample(String stringItem, BigDecimal numberItem, Float floatItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

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

P2: @JsonCreator constructor can override the default non-null list with null when array_item is missing, violating the @NotNull contract for arrayItem.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/model/TypeHolderExample.java, line 47:

<comment>@JsonCreator constructor can override the default non-null list with null when array_item is missing, violating the @NotNull contract for arrayItem.</comment>

<file context>
@@ -44,7 +44,8 @@ public TypeHolderExample() {
    * Constructor with only required parameters
    */
-  public TypeHolderExample(String stringItem, BigDecimal numberItem, Float floatItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
+  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+  public TypeHolderExample(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("float_item") Float floatItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
     this.stringItem = stringItem;
</file context>
Fix with Cubic

public TypeHolderExample(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("float_item") Float floatItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
this.stringItem = stringItem;
this.numberItem = numberItem;
this.floatItem = floatItem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public Apple() {
/**
* Constructor with only required parameters
*/
public Apple(Integer seeds) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Apple(@JsonProperty("seeds") Integer seeds) {
this.seeds = seeds;
this.fruitType = fruitType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public Banana() {
/**
* Constructor with only required parameters
*/
public Banana(Integer length) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Banana(@JsonProperty("length") Integer length) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

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

P2: @JsonCreator constructor lacks a fruitType parameter, so fruitType remains null due to self-assignment, violating required discriminator field.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/server/petstore/spring-boot-oneof-interface/src/main/java/org/openapitools/model/Banana.java, line 41:

<comment>@JsonCreator constructor lacks a fruitType parameter, so fruitType remains null due to self-assignment, violating required discriminator field.</comment>

<file context>
@@ -37,7 +37,8 @@ public Banana() {
    */
-  public Banana(Integer length) {
+  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+  public Banana(@JsonProperty("length") Integer length) {
     this.length = length;
     this.fruitType = fruitType;
</file context>
Fix with Cubic

this.length = length;
this.fruitType = fruitType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public Apple() {
/**
* Constructor with only required parameters
*/
public Apple(Integer seeds) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Apple(@JsonProperty("seeds") Integer seeds) {
this.seeds = seeds;
this.fruitType = fruitType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public Banana() {
/**
* Constructor with only required parameters
*/
public Banana(Integer length) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Banana(@JsonProperty("length") Integer length) {
this.length = length;
this.fruitType = fruitType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public Apple() {
/**
* Constructor with only required parameters
*/
public Apple(Integer seeds) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Apple(@JsonProperty("seeds") Integer seeds) {
this.seeds = seeds;
this.fruitType = fruitType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public Banana() {
/**
* Constructor with only required parameters
*/
public Banana(Integer length) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Banana(@JsonProperty("length") Integer length) {
this.length = length;
this.fruitType = fruitType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public TypeHolderDefault() {
/**
* Constructor with only required parameters
*/
public TypeHolderDefault(String stringItem, BigDecimal numberItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public TypeHolderDefault(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
this.stringItem = stringItem;
this.numberItem = numberItem;
this.integerItem = integerItem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public TypeHolderExample() {
/**
* Constructor with only required parameters
*/
public TypeHolderExample(String stringItem, BigDecimal numberItem, Float floatItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public TypeHolderExample(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("float_item") Float floatItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
this.stringItem = stringItem;
this.numberItem = numberItem;
this.floatItem = floatItem;
Comment on lines +49 to 53
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 28, 2026

Choose a reason for hiding this comment

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

P2: @JsonCreator constructor overwrites the default arrayItem initialization; when array_item is omitted, Jackson passes null and the list becomes null, violating the non-null contract.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/server/petstore/springboot-delegate/src/main/java/org/openapitools/model/TypeHolderExample.java, line 49:

<comment>@JsonCreator constructor overwrites the default `arrayItem` initialization; when `array_item` is omitted, Jackson passes null and the list becomes null, violating the non-null contract.</comment>

<file context>
@@ -46,7 +46,8 @@ public TypeHolderExample() {
    * Constructor with only required parameters
    */
-  public TypeHolderExample(String stringItem, BigDecimal numberItem, Float floatItem, Integer integerItem, Boolean boolItem, List<Integer> arrayItem) {
+  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+  public TypeHolderExample(@JsonProperty("string_item") String stringItem, @JsonProperty("number_item") BigDecimal numberItem, @JsonProperty("float_item") Float floatItem, @JsonProperty("integer_item") Integer integerItem, @JsonProperty("bool_item") Boolean boolItem, @JsonProperty("array_item") List<Integer> arrayItem) {
     this.stringItem = stringItem;
</file context>
Fix with Cubic

Expand Down
Loading
Loading