wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

By Date: May 2022

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 on 07 May 2022 | Comments (0) | categories: Java