Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSR-353 How to add null values using javax.json.JsonObjectBuilder

AS the javax.json docs suggest the way to create a JsonObject is using the provided builders like:

JsonBuilderFactory factory = Json.createBuilderFactory(config);
 JsonObject value = factory.createObjectBuilder()
     .add("firstName", "John")
     .add("lastName", "Smith")
     .add("age", 25)
     .add("address", factory.createObjectBuilder()
         .add("streetAddress", "21 2nd Street")
         .add("city", "New York")
         .add("state", "NY")
         .add("postalCode", "10021"))
     .add("phoneNumber", factory.createArrayBuilder()
         .add(factory.createObjectBuilder()
             .add("type", "home")
             .add("number", "212 555-1234"))
         .add(factory.createObjectBuilder()
             .add("type", "fax")
             .add("number", "646 555-4567")))
     .build();

This example adds values for the gives keys. In real life the values are probably derived from some (pojo) domain object like:

JsonBuilderFactory factory = Json.createBuilderFactory(config);
 JsonObject value = factory.createObjectBuilder()
     .add("firstname", customer.getFirstame())
     .add("lastname", customer.getLastame())
     .add("age", customer.getAge())
     ....

The JsonOBjectBuilder throw a NPE in case key or value is null. In case the customer has no registered age then above code will throw a NPE.

So basically for every field I add I have to check if the value is null or not and add the actual value and otherwise do not add the field or add JsonValue.NULL for the key. This causes a lot of (undesired) boilerplate...

In my case I ended up with custom JsonUtils class including various static methods like:

public static void add(JsonObjectBuilder builder, String name, Long value) {
    if (value == null) {
        builder.add(name,  JsonValue.NULL);
    }
    else {
        builder.add(name, value);
    }
}

public static void add(JsonObjectBuilder builder, String name, String value) {
    if (value == null) {
        builder.add(name,  JsonValue.NULL);
    }
    else {
        builder.add(name, value);
    }
}

and then calling:

builder = Json.createObjectBuilder();
JsonUtils.add(builder, "id", customer.getId());
JsonUtils.add(builder, "name", customer.getName());
JsonUtils.add(builder, "gender", customer.getGender());
builder.build()

But someway if feels not right. Why does the javax.json provide no easier way to add null values (without if else boilerplate) or am I missing something?

My main point of critism against the JSR-353 api is that despite it looks like a really nice fluent api (see top example from apidoc) but in reality it is not.

like image 833
Marcel Overdijk Avatar asked Mar 12 '14 21:03

Marcel Overdijk


People also ask

How do I allow null values in JSON?

To include null values in the JSON output of the FOR JSON clause, specify the INCLUDE_NULL_VALUES option. If you don't specify the INCLUDE_NULL_VALUES option, the JSON output doesn't include properties for values that are null in the query results.

What is javax JSON?

The javax. json package provides an Object Model API to process JSON. The Object Model API is a high-level API that provides immutable object models for JSON object and array structures. These JSON structures can be represented as object models using JsonObject and JsonArray interfaces.


2 Answers

It might be difficult to get your JsonObjectBuilder implementation from the factory but you can make it simpler.

Create a JsonObjectBuilder decorator class that checks for null:

public class NullAwareJsonObjectBuilder implements JsonObjectBuilder {
    // Use the Factory Pattern to create an instance.
    public static JsonObjectBuilder wrap(JsonObjectBuilder builder) {
      if (builder == null) {
        throw new IllegalArgumentException("Can't wrap nothing.");
      }
      return new NullAwareJsonObjectBuilder(builder);
    }

    // Decorated object per Decorator Pattern.
    private final JsonObjectBuilder builder;

    private NullAwareJsonObjectBuilder(JsonObjectBuilder builder) {
      this.builder = builder;
    }

    public JsonObjectBuilder add(String name, JsonValue value) {
      builder.add(name, (value == null) ? JsonValue.NULL : value);
    }

    // Implement all other JsonObjectBuilder methods.
    ..
}

And how you to use it:

JsonObjectBuilder builder = NullAwareJsonObjectBuilder.wrap(
        factory.createObjectBuilder());
builder.add("firstname", customer.getFirstame())
    .add(...
like image 143
Cebence Avatar answered Sep 27 '22 22:09

Cebence


Another possible solution is to use simple and lean helper to wrap a value into JsonValue object or return JsonValue.NULL if value is null.

Here is an example:

public final class SafeJson {
    private static final String KEY = "1";

    //private ctor with exception throwing

    public static JsonValue nvl(final String ref) {
        if (ref == null) {
            return JsonValue.NULL;
        }
        return Json.createObjectBuilder().add(KEY, ref).build().get(KEY);
    }

    public static JsonValue nvl(final Integer ref) {
        if (ref == null) {
            return JsonValue.NULL;
        }
        return Json.createObjectBuilder().add(KEY, ref).build().get(KEY);
    }

    public static JsonValue nvl(final java.sql.Date ref) {
        if (ref == null) {
            return JsonValue.NULL;
        }
        return Json.createObjectBuilder().add(KEY, ref.getTime()).build().get(KEY);
    }
}

Usage is simple:

Json.createObjectBuilder().add("id", this.someId)
    .add("zipCode", SafeJson.nvl(this.someNullableInt))
    .add("phone", SafeJson.nvl(this.someNullableString))
    .add("date", SafeJson.nvl(this.someNullableDate))
    .build(); 

Constructs like Json.createObjectBuilder().add(KEY, ref).build().get(KEY); looks a little bit weird but it is the only portable way to wrap value into JsonValue I've found so far.

In fact in JavaEE8 you can replace it with:

Json.createValue()

or underlying:

JsonProvider.provider().createValue()

call.

like image 45
Bohdan Levchenko Avatar answered Sep 27 '22 22:09

Bohdan Levchenko