Yes No Maybe Boolean deserialization with Jackson
The Robustness principle demands: be lenient in what you accept and strict in what you emit. I was facing this challenge when deserializing boolean values.
What is true
Glancing at data, we can spot, mostly easily what looks trueish:
- true
- "True"
- "Yes"
- 1
- "Si"
- "Ja"
- "Active"
- "isActive"
- "enabled"
- "on"
The last three options aren't as clear cut, they depend on your use case. Using a simple class, lets try to deserialize from JSON to an instance of a Java class instance using Jackson.
Java doesn't have native support for JSON, so we need to rely on libraries like Jackson, Google GSON (or any other listed on the JSON page). I choose Jackson, since it is the library underpinning the JsonObject of the Eclipse Vert.x Framework I'm fond of. Over at Baeldung you will find more generic Jackson tutorials.
Let's look at a simple Java class (Yes, Java14 will make it less verbose), that sports fromJson()
and toJson()
as well as convenient overwrite of equals()
and toString()
package com.notessensei.blogsamples;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.vertx.core.json.JsonObject;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Component {
public static Component fromJson(final JsonObject source) {
return source.mapTo(Component.class);
}
private String name;
private boolean active = false;
public Component() {
// Default empty constructor required
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean getActive() {
return active;
}
public void setActive(boolean isActive) {
this.active = isActive;
}
public JsonObject toJson() {
return JsonObject.mapFrom(this);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Component) {
return this.toString().equals(obj.toString());
}
return super.equals(obj);
}
@Override
public String toString() {
return this.toJson().encode();
}
}
Trying to instantiate a class instance with the following JSON will work:
{
"name": "Heater",
"active": false
}
{
"name": "Aircon"
}
{
"name": "Fridge",
"active": true,
"PowerConsumption": {
"unit": "kw",
"measure": 7
}
}
However it will fail with those:
{
"name": "System1",
"active": "on"
}
{
"name": "System2",
"active": "yes"
}
You get the charming error Cannot deserialize value of type boolean
from String "yes": only "true"/"True"/"TRUE" or "false"/"False"/"FALSE" recognized`. Interestingly numbers work.
On a side note: Jackson uses the presence of getters/setters to decide (de)serialization and needs getActive
and setActive
or isActive
. When you name your variable isActive
Eclipse would generate setActive
and isActive
instead of getIsActive
/ isIsActive
and setIsActive
. So simply avoid the is...
prefix for internal variables.
Read more
Posted by Stephan H Wissel on 07 May 2022 | Comments (0) | categories: Java