fixes(spring) add JsonProperty on setter#22978
fixes(spring) add JsonProperty on setter#22978antechrestos wants to merge 1 commit intoOpenAPITools:masterfrom
Conversation
81ab5b7 to
e397869
Compare
|
@cubic-dev-ai rerun a review |
@antechrestos I have started the AI code review. It will take a few minutes to complete. |
|
@wing328 here's the fix I was waiting to push after the merge of #22854 As suggested in the issue #22757 , I applied the fix that already is present on java side; also, I used partial so as not to repeat myself. There are tons of modified files yet difference consist in thee diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/jackson_annotations.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/jackson_annotations.mustache
new file mode 100644
index 00000000000..0668f40785c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/jackson_annotations.mustache
@@ -0,0 +1,7 @@
+ @JsonProperty("{{baseName}}")
+{{#withXml}}
+ @JacksonXmlProperty(localName = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#isXmlAttribute}}, isAttribute = true{{/isXmlAttribute}}{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
+ {{#isContainer}}
+ @JacksonXmlElementWrapper({{#isXmlWrapped}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}", {{#xmlNamespace}}namespace = "{{.}}", {{/xmlNamespace}}{{/isXmlWrapped}}useWrapping = {{isXmlWrapped}})
+ {{/isContainer}}
+{{/withXml}}
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache
index 82a0a6eb00c..8a93fb054f1 100644
--- a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache
@@ -226,25 +226,10 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
{{#swagger1AnnotationLibrary}}
@ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}")
{{/swagger1AnnotationLibrary}}
- {{#jackson}}
- @JsonProperty("{{baseName}}")
- {{#withXml}}
- @JacksonXmlProperty(localName = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#isXmlAttribute}}, isAttribute = true{{/isXmlAttribute}}{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
- {{#isContainer}}
- @JacksonXmlElementWrapper({{#isXmlWrapped}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}", {{#xmlNamespace}}namespace = "{{.}}", {{/xmlNamespace}}{{/isXmlWrapped}}useWrapping = {{isXmlWrapped}})
- {{/isContainer}}
- {{/withXml}}
- {{/jackson}}
- {{#withXml}}
- @Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
- {{#isXmlWrapped}}
- @XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
- {{/isXmlWrapped}}
- {{/withXml}}
{{#deprecated}}
@Deprecated
{{/deprecated}}
- public {{>nullableAnnotation}}{{>nullableDataTypeBeanValidation}} {{getter}}() {
+{{#jackson}}{{>jackson_annotations}}{{/jackson}}{{#withXml}}{{>xmlAccessorAnnotation}}{{/withXml}} public {{>nullableAnnotation}}{{>nullableDataTypeBeanValidation}} {{getter}}() {
return {{name}};
}
{{/lombok.Getter}}
@@ -261,7 +246,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
{{#deprecated}}
@Deprecated
{{/deprecated}}
- public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
+{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{>jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
this.{{name}} = {{name}};
}
{{/lombok.Setter}}
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/xmlAccessorAnnotation.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/xmlAccessorAnnotation.mustache
new file mode 100644
index 00000000000..ee5195c0eb5
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/xmlAccessorAnnotation.mustache
@@ -0,0 +1,4 @@
+ @Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
+{{#isXmlWrapped}}
+ @XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
+{{/isXmlWrapped}} |
|
Thanks for the PR cc @cachescrubber (2022/02) @welshm (2022/02) @MelleD (2022/02) @atextor (2022/02) @manedev79 (2022/02) @javisst (2022/02) @borsch (2022/02) @banlevente (2022/02) @Zomzog (2022/09) @martin-mfg (2023/08) |
21562c7 to
c00186a
Compare
|
@wing328 I was wondering why previous run of Sample Java Spring were not launched. When taking a look at the action page, I saw that it was only launched on release 😨 To cap it all, one of the path was wrong. To fix it, I made the following fixes and now job are launched. |
|
@wing328 @cachescrubber @welshm @MelleD @atextor @manedev79 @javisst @borsch @Zomzog @martin-mfg Any feed back ? 😏 |
|
@antechrestos I've found another issue with jackson 3. By default, a constructor with argument is used to deserialize. So if there is a required arguments or an all arguments constructor, it will take precedence. For example: Jackson uses the constructor and fails to initializer xRequestId because there is no The issue is very bad for lists: Deserializing It is possible to configure the JsonMapper with A simpler and more robust solution is to annotated the constructor with Can you add the annotation at line 105 and 128 in pojo.mustache? It won't work for lombok generated constructor though. |
|
@jpfinne To my point of view, I think that the setter approach is quite a bad pattern. I would better
This would provide immutability which is not a bad approach. As an alternative so as have not that much breaking change, I would propose something like public class TestData {
private String someStringAttribute;
private List<String> list = new ArrayList<>();
// so as not to break code
public TestData() {
}
@JsonCreator // this will force jackson to use this, good semantic, this will host deserialisation declaration
public TestData(
// here for deserialization
@JsonProperty("someStringAttribute") String someStringAttribute,
// here for deserialization
@JsonProperty("list") List<String> list
) {
this.someStringAttribute = someStringAttribute;
this.list = list;
}
// here for serialization
@JsonProperty("someStringAttribute")
public String getSomeStringAttribute() {
return this.someStringAttribute;
}
// here for serialization
@JsonProperty("list")
public void getList() {
return this.list ;
}
// just for coder using noarg constructor
public void setSomeStringAttribute(String someStringAttribute) {
this.someStringAttribute= someStringAttribute;
}
// just for coder using noarg constructor
public void setList(List<String> list) {
this.list = list;
}
}This approach is the one that enforce requirement when you set What do you think of it? |
|
@antechrestos I've tried this approch. My PR gave hundred of errors from cubic-dev-ai. So I close it. The reason is that the assignments in the constructor must assign a value equivalent to the default value in the field declaration when the argument is null. In your example: The default values for
The constructor generation depends on:
Good luck to keep backward compatibility in all java generators that use jackson. Any breaking change triggers an avalanche of criticism. So I would go for |
|
@jpfinne that is sad because this is the right way of deserializing in jackson. Moreover this way lets you handle require fields using the As an example the following code illustrate this mechanism package org.demo.jackson.deserialisation;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
import java.util.List;
import java.util.Optional;
public class JacksonDeserializationTest {
public static class Payload {
private String xRequestId;
private List<String> xRequestIds;
@JsonProperty("x-request-id")
public String getxRequestId() {
return xRequestId;
}
@JsonProperty("x-request-ids")
public List<String> getxRequestIds() {
return xRequestIds;
}
public Payload() {
}
public Payload(String xRequestId) {
this.xRequestId = xRequestId;
}
public Payload(List<String> xRequestIds) {
this.xRequestIds = xRequestIds;
}
@JsonCreator
public Payload(
@JsonProperty("x-request-id") String xRequestId,
@JsonProperty("x-request-ids") List<String> xRequestIds
) {
this.xRequestId = xRequestId;
this.xRequestIds = xRequestIds;
JacksonDeserializationTest.byAllArgConstructor = true;
}
}
static boolean byAllArgConstructor = false;
private static final String JSON = """
{
"x-request-id": "the-request-id",
"x-request-ids": ["the-first-request-id", "the-second-request-id"]
}
""";
private static void checkJackson2() {
byAllArgConstructor = false;
try {
Payload payload = new ObjectMapper().readValue(JSON, Payload.class);
checkAssertions(payload);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
private static void checkJackson3() {
byAllArgConstructor = false;
var payload = JsonMapper.builder().build().readValue(JSON, JacksonDeserializationTest.Payload.class);
checkAssertions(payload);
}
private static void checkAssertions(Payload payload) {
assert "the-request-id".equals(payload.xRequestId);
assert 2 == Optional.ofNullable(payload.xRequestIds).map(List::size).orElse(0);
assert "the-first-request-id".equals(payload.xRequestIds.get(0));
assert "the-second-request-id".equals(payload.xRequestIds.get(1));
assert byAllArgConstructor;
}
public static void main(String[] args) {
System.out.println("Testing jackson 2...");
checkJackson2();
System.out.println("Jackson 2 OK");
System.out.println("Testing jackson 3...");
checkJackson3();
System.out.println("Jackson 3 OK");
}
} |
|
backward and forward compatibility is more important |
* extract jacksonAnnotation partial template * extract xmkAccessorAnnotation partial template * apply jacksonAnnotation partial template on both getter and setter Fixes OpenAPITools#22757
c00186a to
942ec96
Compare
|
what I don't understand is that with jackson3 and jackson 2, without any public class JacksonDeserializationTest {
public static class Payload {
private UUID xRequestId;
private List<UUID> xRequestIds;
private List<String> unknwonProperty = new ArrayList<>();
@JsonProperty("x-request-id")
public UUID getxRequestId() {
return xRequestId;
}
@JsonProperty("x-request-ids")
public List<UUID> getxRequestIds() {
return xRequestIds;
}
@JsonProperty("unknown-property")
public List<String> getUnknwonProperty() {
return unknwonProperty;
}
@JsonProperty("x-request-id")
public void setxRequestId(UUID xRequestId) {
this.xRequestId = xRequestId;
}
@JsonProperty("x-request-ids")
public void setxRequestIds(List<UUID> xRequestIds) {
this.xRequestIds = xRequestIds;
}
@JsonProperty("unknown-property")
public void setUnknwonProperty(List<String> unknwonProperty) {
this.unknwonProperty = unknwonProperty;
}
public Payload() {
}
public Payload(UUID xRequestId) {
this.xRequestId = xRequestId;
}
public Payload(List<UUID> xRequestIds) {
this.xRequestIds = xRequestIds;
}
public Payload(
UUID xRequestId,
List<UUID> xRequestIds,
List<String> unknwonProperty
) {
this.xRequestId = xRequestId;
this.xRequestIds = xRequestIds;
this.unknwonProperty = unknwonProperty;
}
}
private static final String JSON = """
{
"x-request-id": "9f09113b-1f95-4c8a-b90a-c5bf6c35c4d9",
"x-request-ids": ["9f09113b-1f95-4c8a-b90a-c5bf6c35c4da", "9f09113b-1f95-4c8a-b90a-c5bf6c35c4db"]
}
""";
private static void checkJackson2() {
try {
Payload payload = new ObjectMapper().readValue(JSON, Payload.class);
checkAssertions(payload);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
private static void checkJackson3() {
var payload = JsonMapper.builder().build().readValue(JSON, JacksonDeserializationTest.Payload.class);
checkAssertions(payload);
}
private static void checkAssertions(Payload payload) {
assert "the-request-id".equals(payload.xRequestId);
assert 2 == Optional.ofNullable(payload.xRequestIds).map(List::size).orElse(0);
assert "the-first-request-id".equals(payload.xRequestIds.get(0));
assert "the-second-request-id".equals(payload.xRequestIds.get(1));
assert payload.unknwonProperty != null;
assert payload.unknwonProperty.size() == 1;
}
public static void main(String[] args) {
try{
System.out.println("Testing jackson 2...");
checkJackson2();
System.out.println("Jackson 2 OK");
} catch (Throwable t){
System.err.println("Jackson 2 KO");
t.printStackTrace(System.err);
}
try {
System.out.println("Testing jackson 3...");
checkJackson3();
System.out.println("Jackson 3 OK");
} catch (Throwable t){
System.err.println("Jackson 3 KO");
t.printStackTrace(System.err);
}
}
}So I think going with the annotated setters as on the java generator will do the job... mostly. For list initiated in the field resulting in As you mentionned, refactoring the constructor handling lombok annotations would be tricky and error prone, while adding the annotation on setter is mature thanks to the java generator which already does it. I repush my change with a mutualisation of of jackson annotation which were in the jacksonAnnotation partial mustache file. |
|
Modified files And the differences diff --git a/.github/workflows/samples-spring-jdk17.yaml b/.github/workflows/samples-spring-jdk17.yaml
index 9e48ff2f74d..daf09a63412 100644
--- a/.github/workflows/samples-spring-jdk17.yaml
+++ b/.github/workflows/samples-spring-jdk17.yaml
@@ -3,32 +3,32 @@ name: Samples Java Spring (JDK17)
on:
push:
paths:
- - samples/openapi3/client/petstore/spring-cloud-3-with-optional
- - samples/openapi3/client/petstore/spring-cloud-4-with-optional
- - samples/client/petstore/spring-http-interface-springboot-4
- - samples/openapi3/server/petstore/springboot-3
- - samples/openapi3/server/petstore/springboot-4
- - samples/server/petstore/springboot-api-response-examples
- - samples/server/petstore/springboot-lombok-data
- - samples/server/petstore/springboot-lombok-tostring
- - samples/server/petstore/springboot-file-delegate-optional
- - samples/server/petstore/springboot-petstore-with-api-response-examples
- - samples/server/petstore/spring-boot-oneof-sealed
- - samples/openapi3/server/petstore/spring-boot-oneof-interface
+ - samples/openapi3/client/petstore/spring-cloud-3-with-optional/**
+ - samples/openapi3/client/petstore/spring-cloud-4-with-optional/**
+ - samples/client/petstore/spring-http-interface-springboot-4/**
+ - samples/openapi3/server/petstore/springboot-3/**
+ - samples/openapi3/server/petstore/springboot-4/**
+ - samples/server/petstore/springboot-api-response-examples/**
+ - samples/server/petstore/springboot-lombok-data/**
+ - samples/server/petstore/springboot-lombok-tostring/**
+ - samples/server/petstore/springboot-file-delegate-optional/**
+ - samples/server/petstore/springboot-petstore-with-api-response-examples/**
+ - samples/openapi3/server/petstore/spring-boot-oneof-sealed/
+ - samples/openapi3/server/petstore/spring-boot-oneof-interface/**
pull_request:
paths:
- - samples/openapi3/client/petstore/spring-cloud-3-with-optional
- - samples/openapi3/client/petstore/spring-cloud-4-with-optional
- - samples/client/petstore/spring-http-interface-springboot-4
- - samples/openapi3/server/petstore/springboot-3
- - samples/openapi3/server/petstore/springboot-4
- - samples/server/petstore/springboot-api-response-examples
- - samples/server/petstore/springboot-lombok-data
- - samples/server/petstore/springboot-lombok-tostring
- - samples/server/petstore/springboot-file-delegate-optional
- - samples/server/petstore/springboot-petstore-with-api-response-examples
- - samples/server/petstore/spring-boot-oneof-sealed
- - samples/openapi3/server/petstore/spring-boot-oneof-interface
+ - samples/openapi3/client/petstore/spring-cloud-3-with-optional/**
+ - samples/openapi3/client/petstore/spring-cloud-4-with-optional/**
+ - samples/client/petstore/spring-http-interface-springboot-4/**
+ - samples/openapi3/server/petstore/springboot-3/**
+ - samples/openapi3/server/petstore/springboot-4/**
+ - samples/server/petstore/springboot-api-response-examples/**
+ - samples/server/petstore/springboot-lombok-data/**
+ - samples/server/petstore/springboot-lombok-tostring/**
+ - samples/server/petstore/springboot-file-delegate-optional/**
+ - samples/server/petstore/springboot-petstore-with-api-response-examples/**
+ - samples/openapi3/server/petstore/spring-boot-oneof-sealed/**
+ - samples/openapi3/server/petstore/spring-boot-oneof-interface/**
jobs:
build:
name: Build Java Spring (JDK17)
@@ -49,7 +49,7 @@ jobs:
- samples/server/petstore/springboot-lombok-tostring
- samples/server/petstore/springboot-file-delegate-optional
- samples/server/petstore/springboot-petstore-with-api-response-examples
- - samples/server/petstore/spring-boot-oneof-sealed
+ - samples/openapi3/server/petstore/spring-boot-oneof-sealed
- samples/openapi3/server/petstore/spring-boot-oneof-interface
steps:
- uses: actions/checkout@v5
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/jackson_annotations.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/jackson_annotations.mustache
new file mode 100644
index 00000000000..0668f40785c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/jackson_annotations.mustache
@@ -0,0 +1,7 @@
+ @JsonProperty("{{baseName}}")
+{{#withXml}}
+ @JacksonXmlProperty(localName = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#isXmlAttribute}}, isAttribute = true{{/isXmlAttribute}}{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
+ {{#isContainer}}
+ @JacksonXmlElementWrapper({{#isXmlWrapped}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}", {{#xmlNamespace}}namespace = "{{.}}", {{/xmlNamespace}}{{/isXmlWrapped}}useWrapping = {{isXmlWrapped}})
+ {{/isContainer}}
+{{/withXml}}
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/lombokAnnotation.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/lombokAnnotation.mustache
index 50834e20238..6b09b602fe2 100644
--- a/modules/openapi-generator/src/main/resources/JavaSpring/lombokAnnotation.mustache
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/lombokAnnotation.mustache
@@ -23,14 +23,7 @@
{{#swagger2AnnotationLibrary}}
@Schema(name = "{{{baseName}}}"{{#isReadOnly}}, accessMode = Schema.AccessMode.READ_ONLY{{/isReadOnly}}{{#example}}, example = "{{{.}}}"{{/example}}{{#description}}, description = "{{{.}}}"{{/description}}{{#deprecated}}, deprecated = true{{/deprecated}}, requiredMode = {{#required}}Schema.RequiredMode.REQUIRED{{/required}}{{^required}}Schema.RequiredMode.NOT_REQUIRED{{/required}})
{{/swagger2AnnotationLibrary}}
- {{#jackson}}@JsonProperty("{{baseName}}")
- {{#withXml}}
- @JacksonXmlProperty(localName = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#isXmlAttribute}}, isAttribute = true{{/isXmlAttribute}}{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
- {{#isContainer}}
- @JacksonXmlElementWrapper({{#isXmlWrapped}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}", {{#xmlNamespace}}namespace = "{{.}}", {{/xmlNamespace}}{{/isXmlWrapped}}useWrapping = {{isXmlWrapped}})
- {{/isContainer}}
- {{/withXml}}
- {{/jackson}}
+{{#jackson}}{{>jackson_annotations}}{{/jackson}}
{{/lombok.Data}}
{{#lombok.Builder}}
{{#defaultValue}}
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache
index 82a0a6eb00c..8a93fb054f1 100644
--- a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache
@@ -226,25 +226,10 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
{{#swagger1AnnotationLibrary}}
@ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}")
{{/swagger1AnnotationLibrary}}
- {{#jackson}}
- @JsonProperty("{{baseName}}")
- {{#withXml}}
- @JacksonXmlProperty(localName = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#isXmlAttribute}}, isAttribute = true{{/isXmlAttribute}}{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
- {{#isContainer}}
- @JacksonXmlElementWrapper({{#isXmlWrapped}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}", {{#xmlNamespace}}namespace = "{{.}}", {{/xmlNamespace}}{{/isXmlWrapped}}useWrapping = {{isXmlWrapped}})
- {{/isContainer}}
- {{/withXml}}
- {{/jackson}}
- {{#withXml}}
- @Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
- {{#isXmlWrapped}}
- @XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
- {{/isXmlWrapped}}
- {{/withXml}}
{{#deprecated}}
@Deprecated
{{/deprecated}}
- public {{>nullableAnnotation}}{{>nullableDataTypeBeanValidation}} {{getter}}() {
+{{#jackson}}{{>jackson_annotations}}{{/jackson}}{{#withXml}}{{>xmlAccessorAnnotation}}{{/withXml}} public {{>nullableAnnotation}}{{>nullableDataTypeBeanValidation}} {{getter}}() {
return {{name}};
}
{{/lombok.Getter}}
@@ -261,7 +246,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}
{{#deprecated}}
@Deprecated
{{/deprecated}}
- public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
+{{#jackson}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{>jackson_annotations}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{/jackson}} public void {{setter}}({{>nullableAnnotation}}{{>nullableDataType}} {{name}}) {
this.{{name}} = {{name}};
}
{{/lombok.Setter}}
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/xmlAccessorAnnotation.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/xmlAccessorAnnotation.mustache
new file mode 100644
index 00000000000..ee5195c0eb5
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/xmlAccessorAnnotation.mustache
@@ -0,0 +1,4 @@
+ @Xml{{#isXmlAttribute}}Attribute{{/isXmlAttribute}}{{^isXmlAttribute}}Element{{/isXmlAttribute}}(name = "{{items.xmlName}}{{^items.xmlName}}{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}{{/items.xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
+{{#isXmlWrapped}}
+ @XmlElementWrapper(name = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}"{{#xmlNamespace}}, namespace = "{{.}}"{{/xmlNamespace}})
+{{/isXmlWrapped}} |
|
@antechrestos your test is not realistic. It has 4 constructors. I guess jackson3 does not know which constructor to use and use the setters. About your sentence "For list initiated in the field resulting in null while deserializing a payload without any entry of the field, I would expect it to be the normal behaviour". This is the behaviour when using The default is false, so the deserialization by the constructor is a breaking change for lists. So please add |
|
@jpfinne the problem is that even the no arg constructor has a condition around it. I am not sure that putting an annotation around all constructor having a parameter will be OK for everyone. Same for the option. Moreover without real example. Putting the annotation on setter is a real pain that already was present in jackson 2: in the example I provided, that is jackson 2 which fails. The fix is almost without risk as it should have been ported in spring generator when it was put in Java generator. The person in the issue pointed that this fix resolves the issue. If there are other issues, I'd rather work on it on a dedicated pr, after having worked on input that illustrates the issue. That is provided that I get any feedback by a maintainer 😏 |
In order to fix #22757, I applied same logic used on java side by #9041
jacksonAnnotationpartial templatexmlAccessorAnnotationpartial templatejacksonAnnotationandxmlAccessorAnnotationpartial templates on both getter and setterSummary by cubic
Add @JsonProperty to generated Java Spring model setters so Jackson maps JSON names correctly during deserialization. Apply the same annotation block to getters and XML accessors, excluding properties marked as jackson-optional-nullable (fixes #22757).
Bug Fixes
Refactors
Written for commit 942ec96. Summary will update on new commits.