-
-
Notifications
You must be signed in to change notification settings - Fork 6.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Java][client] make it possible to send explicit nulls for nullable fields #3474
[Java][client] make it possible to send explicit nulls for nullable fields #3474
Conversation
ff21516
to
6c1f971
Compare
Please see my comment in #3435 . Wouldn't JsonNullable be a good fit for this ? |
@cbornet I added a commit that removes the custom Optional implementation that I did and instead uses JsonNullable. Thanks for the suggestion, it works like a charm. Some more things I still need to do:
|
{{> jackson_annotations}} | ||
{{/jackson}} | ||
{{#vendorExtensions.isJacksonOptionalNullable}} | ||
private JsonNullable<{{{datatypeWithEnum}}}> {{name}} = JsonNullable.{{#defaultValue}}of({{{.}}}){{/defaultValue}}{{^defaultValue}}undefined(){{/defaultValue}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need for a vendor extension ? I think it can be wrapped with JsonNullable as soon as the field is "nullable".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was my line of thinking with the vendor extension:
I needed to have a bunch of "if and else" conditions in the pojo template to determine if the context have jackson
, !required
and nullable
set at the same time. I could theoretically drop the !required
as condition (even though I like having the simplicity of going with JsonInclude.Include.ALWAYS
for required fields), but I can't drop the jackson
as condition.
If I didn't have this as one variable in the mustache rendering context, it'd be hard doing the "else" part. Additionally, since jackson
is Java specific, I decided to use a "vendor" variable for this, as the other uses of vendor extensions in other codegens seemed to be similar to what I'm doing here.
I'm of course ok with improving this if you have a better idea.
@cbornet hmm, so I noticed that this isn't really working. The "explicit nulls" (IOW JsonNullable that is defined and set to null) are not serialized as null. Any idea on what I'm doing wrong? I can't seem to make it work. |
Right, so it seems that this works when I use the jackson annotations on the |
Fixed and seems to work fine. This is now pretty much ready from my side, except I'll still have to edit pom.xml/JSON.java templates in other generators using jackson and also actually regenerate all affected samples. |
As requested, I'm sharing a simple example of what a generated pojo would look like. There are some extraneous whitespaces that I need to get rid of in my changes, but everything seems to work. (Note that none of the samples really illustrate this good and I can't share my real work yet, so this is a made up example). The biggest changes are on optional-nullable fields and maps that contain null values, so I'm sharing an example with these two. [edit] note that one more notable change is not providing public class MySchema {
// mymap has additionalProperties that are nullable
private Map<String, Long> mymap = new HashMap<String, Long>();
// end is a nullable, not required Long
private JsonNullable<Long> end = JsonNullable.undefined();
public MonitorOptions mymap(Map<String, Long> mymap) {
this.mymap = mymap;
return this;
}
public MonitorOptions putMymapItem(String key, Long mymapItem) {
if (this.mymap == null) {
this.mymap = new HashMap<String, Long>();
}
this.mymap.put(key, mymapItem);
return this;
}
/**
* Get mymap
* @return mymap
**/
@javax.annotation.Nullable
@ApiModelProperty(value = "")
@JsonProperty("mymap")
@JsonInclude(content = JsonInclude.Include.ALWAYS, value = JsonInclude.Include.USE_DEFAULTS)
public Map<String, Long> getMymap() {
return mymap;
}
public void setMymap(Map<String, Long> mymap) {
this.mymap = mymap;
}
public Downtime end(Long end) {
this.end = JsonNullable.of(end);
return this;
}
@JsonProperty("end")
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
public JsonNullable<Long> getEnd_JsonNullable() {
return end;
}
public void setEnd_JsonUndefined() {
this.end = JsonNullable.undefined();
}
/**
* Get end
* @return end
**/
@javax.annotation.Nullable
@ApiModelProperty(example = "1412793983", value = "")
public Long getEnd() {
return end.orElse(null);
}
public void setEnd(Long end) {
this.end = JsonNullable.of(end);
}
} |
I pushed some additional commits:
|
3785911
to
0ac57ad
Compare
0ac57ad
to
b551043
Compare
(Sorry for so many force-pushes, I'm just trying to squash all the bugs/typos... I've already figured out that I can run the tests for the regenerated libraries individually on my laptop, so I'll try to fix everything before the next force push which will hopefully also be the last one.) |
b551043
to
be42b7c
Compare
No worry. If you prefer, you can also push multiple mini-commits to your branch. At the end all of them will be squashed into one single commit on the master branch. |
So finally all tests are passing. Can I get another round of review? Thanks! CC @cbornet |
I think JsonNullable might be confusing if someone uses I kinda wish the |
So I agree with you on the naming part, but there's not much I can do about that unless I wanted to radically change the jackson-databind-nullable library. I have to admit I didn't try to use this with XML endpoints. I was assuming that jackson would automagically do the correct thing, but I do agree that it's something to test for sure.
Yeah, I guess that would be better from this perspective, but I honestly consider that to be out of scope of this PR, as it's already complex as it is. |
Is this feature always included, or is it something the user can opt-in/opt-out? I see that some code in the template is guarded with But not all of them. |
This feature is always included. The Hope this makes it clearer. I'd be happy to add some comments if that would improve the situation. |
Just to make the above statement clearer: this feature is always included for Java clients that use jackson as their parsing library - with the exception of jersey1, which doesn't work with new enough version of jackson (which is required for JsonNullable to work). |
I'm wondering if it might address everyone's comments in the PR to change the vendor extension to a generator option |
@jimschubert yeah, I think that sounds fair. I can do that. FTR, what do you think about making it default tu |
@bkabrda I think defaulting to true is fine. In minor releases, we allow breaking changes with fallback. I think without a user facing option, this would break all existing Java clients meaning a code change in a minor release. The option (as I understand the changes) allows a user to fallback to similar code as before. |
As always I'm reluctant to add yet another option. If we think that's the best way to handle nullables then we should enforce it. |
Ok folks, I really appreciate all the feedback that I received, so thanks for that. As for adding/not adding the new option, I guess that's your decision and I will implement whatever way you want. Let me try to sum up the pros of both ways: Pros of having a new option:
Pros of not having a new option:
Personally I would prefer to not have a new option, since the existing methods of the generated POJOs stay stable, so there is no breaking change to their API - there are only additions. The only thing left to verify is what the behavior is for XML clients, I'll try to do that today (but no promises, this PR has unfortunately already eaten too much of my time and I have to attend to other things as well). |
@jimschubert @cbornet alternatively, if it seems that there's no agreement on the form in which the feature is accepted, would it make sense to:
I'd love to do at least that, since that would make it possible for me to not have to build my own openapi-generator - I would only have to use my custom templates, which is much easier to do for my usecase. Would that make sense to you? I don't mean to bother you too much, but I really need to make this work in a reasonable way for a project I'm working on. Thanks for considering and thanks for keeping the discussion constructive 👍 |
So after some testing, it seems that JsonNullable doesn't serialize correctly to XML right now. This makes me think that we should adopt the approach with 2 PRs that I noted above and implement XML serialization to JsonNullable before the second PR can be merged. I'll open an RFE for JsonNullable in the meanwhile. Does this sound good to everyone? |
Ok, so the tests are passing now and I feel like I've addressed all the comments that were raised. Please let me know if this is ok to be merged or if there's something more that I need to improve on. |
6b240c0
to
d411d66
Compare
Rebased and pushed - let's wait to see if the tests pass. I actually found out one minor bug while testing this PR and fixed it - this made me realize that there are actually no tests for OpenAPI 3 Java clients. I did as much testing as I could, but this is pretty complex and someone could break it with their PRs. When this is merged, I'd love to write tests, but can't guarantee 100 % that I'll have time to do so, so I'd appreciate if someone could help with this (testing various combinations of nullable/required/readOnly/container fields and how they behave during serialization would be great). |
@bkabrda can you please elaborate a bit more on the bug you fixed? |
d411d66
to
4dad4d5
Compare
@wing328 it was related to #3615 - previously, I was assuming in |
4dad4d5
to
63df67b
Compare
(Oh, I just realized I might have misunderstood your question. I didn't find a bug that was already in current code of openapi-generator, I found a bug that was result of combining PR #3615 with this PR when I rebased. That's why I said we should have tests for openapi 3 for Java clients.) |
Totally agreed. I added new some tests earlier this week: #3640 Please feel free to add more later with a separate PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested lcoally and the code compiles without issues
I am going to present something based on Mock-Server in #689, feel free to follow this issue and/or to add your comment. I am sorry I could not test this before it was merged, so I am reviewing the impact of this PR now. I observe I observe @bkabrda I hope you can elaborate on those points |
Yes, It's intended. It was required for the JsonNullable attribute (I couldn't get this to work with JsonProperty on the JsonNullable attribute itself for some reason, but I couldn't figure out why). It is fully backwards compatible AFAICS.
Yeah, I used this to make sure we have explicit statement on what the behavior is on inclusion of all fields to make sure the behavior is stable. I probably could have assumed that |
I did some tests with:
Both worked for me. About using all the Json annotations in the model or not, I am not sure. This Jackson-annotation topic goes also behind this PR: |
The problem is that |
Yeah. I'll admit I'm not familiar with gson too much (and Java in general TBH), so making this PR work just for jackson was complex enough for me that I didn't want to go into it. The way I understand it, it might be possible to make JsonNullable compatible with gson - I think that would be preferrable to using a different nullable implementation for each serialization library. @jmini if you see any specific followup things to fix, please let me know, I'll be happy to look into them. Thanks! |
@bkabrda thanks for the PR, which has been included in the v4.1.1 release: https://twitter.com/oas_generator/status/1165944867391860737 |
The new dependency was not added to the generated |
Hello all, From my perspective, as a user of Thanks! |
PR checklist
./bin/
to update Petstore sample so that CIs can verify the change. (For instance, only need to run./bin/{LANG}-petstore.sh
,./bin/openapi3/{LANG}-petstore.sh
if updating the {LANG} (e.g. php, ruby, python, etc) code generator or {LANG} client's mustache templates). Windows batch files can be found in.\bin\windows\
. If contributing template-only or documentation-only changes which will change sample output, be sure to build the project first.master
,4.1.x
,5.0.x
. Default:master
.CC @bbdouglas (2017/07) @sreeshas (2017/08) @jfiala (2017/08) @lukoyanov (2017/09) @cbornet (2017/09) @jeff9finger (2018/01) @karismann (2019/03) @Zomzog (2019/04)
Description of the PR
This is my attempt to fix #3435. Basically it makes sure that explicit
null
values can be sent for nullable fields:null
is always sent when that's the value.Optional
class withOptionalFilter
that make sure that thenull
is sent if and only if it was set explicitly. AFAICS, the API of any of the generated classes didn't change in a backwards-incompatible way.Discussion points:
Optional
sincejava.util.Optional
was added in JDK 8 and the templates still have support for previous Java versions. If desired, I can replace this with a different (third party) implementation pretty easily../bin/...
generators yet to keep the PR clean and reviewable. I'm planning to run it later.