jackson-databind
jackson-databind copied to clipboard
Distinguish between null and non-set field when deserializing to Java 16 record
In case of deserialization to Java 16 record is there any way to distinguish between situation when JSON field is set to null and when field is not set at all. For example using jackson-databind-nullable or Java's Optional ?
This is not a user question forum: please use mailing list:
https://groups.google.com/g/jackson-user
or chat
https://gitter.im/FasterXML/jackson-databind
for asking about usage.
Or, if you have specific use case that does not way you'd expect, you could change this issue to include that.
(use of Optional
is supported for Record types just fine)
@cowtowncoder Thanks for the info. Asked on the gitter.
Note: This is not about whether Optional
is supported in records or not.
As discussed on Gitter: https://gitter.im/FasterXML/jackson-databind?at=61eb1a995dc6213cd4f3550e this seems not to be easily achievable. Therefore I believe this issue should stay as bug / request for extension. Here is minimal example that shows this problem: https://github.com/hajdamak/jackson-record-optional
I am not 100% sure what the proposed solution would be here, so it would be good to outline that here.
I am guessing this might be to try to explicitly pass null
for missing value for Optional<x>
passed via Creator method; as opposed to Optional.empty()
that'd be passed for explicit incoming JSON null
. Is this what'd be expected?
Put another way, a unit test would make it easy to deduce intent.
I'm not sure if this is a proper solution or a hack, but here's what I can think of:
public class App {
public static final String NOT_PROVIDED_MARKER_ID = "__NOT_PROVIDED";
public static void main(String[] args) throws Exception {
record Car(String color, String type) {
Car(@JacksonInject(NOT_PROVIDED_MARKER_ID) String color, String type) {
this.color = color;
this.type = type;
}
public boolean isColorProvided() {
return !NOT_PROVIDED_MARKER_ID.equals(color);
}
@Override
public String color() {
return isColorProvided() ? color : null;
}
}
ObjectMapper objectMapper = new ObjectMapper().setInjectableValues(new InjectableValues.Std().addValue(NOT_PROVIDED_MARKER_ID, NOT_PROVIDED_MARKER_ID));
String[] jsons = {
"{\"color\":null,\"type\":\"Solaris\"}",
"{\"color\":\"Red\",\"type\":\"Solaris\"}",
"{\"type\":\"Solaris\"}"
};
for (String json : jsons) {
Car car = objectMapper.readValue(json, Car.class);
System.out.printf("[%s] color '%s' was provided: %s\n", car.toString(), car.color(), car.isColorProvided());
}
}
Output:
[Car[color=null, type=Solaris]] color 'null' was provided: true
[Car[color=Red, type=Solaris]] color 'Red' was provided: true
[Car[color=__NOT_PROVIDED, type=Solaris]] color 'null' was provided: false
Caveat: Your car.toString()
will show color
as __NOT_PROVIDED
.
I am guessing this might be to try to explicitly pass null for missing value for Optional
passed via Creator method; as opposed to Optional.empty() that'd be passed for explicit incoming JSON null.
Having an optional == null
seems to be going against the idea of Optional
... 😰
I'm assuming you're currently doing something like this using non-Records class:
public static class Car {
private boolean colorProvided = false;
private String color;
...
public void setColor(String color) {
this.colorProvided = true;
this.color = color;
}
...
public boolean isColorProvided() {
return colorProvided;
}
}
If so, I think you should just stick to that. Trying to fit this special scenario into Records feels like trying to fit square peg in a round hole.