JSR-353 Как добавить нулевые значения с помощью javax.json.JsonObjectBuilder

AS в документах javax.json предлагают способ создания JsonObject с использованием предоставленных сборщиков, таких как:

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();

В этом примере добавляются значения для клавиш, указывающих. В реальной жизни значения, вероятно, происходят от некоторого (pojo) объекта домена, такого как:

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

JsonOBjectBuilder бросает NPE в случае, если ключ или значение равно null. В случае, если у клиента нет зарегистрированного возраста, тогда над кодом будет выброшен NPE.

Итак, в основном для каждого добавляемого поля я должен проверить, является ли значение нулевым или нет, и добавить фактическое значение, и в противном случае не добавлять это поле или добавить JsonValue.NULL для ключа. Это вызывает много (нежелательных) шаблонов...

В моем случае я закончил с обычным классом JsonUtils, включая различные статические методы, такие как:

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);
    }
}

а затем вызов:

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

Но каким-то образом, если это не так. Почему javax.json не предоставляет более простой способ добавления нулевых значений (без if else templateplate) или я что-то не хватает?

Моя главная точка критики против JSR-353 api заключается в том, что, несмотря на то, что она выглядит как действительно хорошая беглое api (см. пример сверху из apidoc), но на самом деле это не так.

Ответ 1

Трудно получить реализацию JsonObjectBuilder из factory, но вы можете сделать ее проще.

Создайте класс декоратора JsonObjectBuilder, который проверяет 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.
    ..
}

И как вы его используете:

JsonObjectBuilder builder = NullAwareJsonObjectBuilder.wrap(
        factory.createObjectBuilder());
builder.add("firstname", customer.getFirstame())
    .add(...

Ответ 2

Другим возможным решением является использование простого и худшего помощника для переноса значения в объект JsonValue или return JsonValue.NULL, если значение равно null.

Вот пример:

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);
    }
}

Использование прост:

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(); 

Конструкции типа Json.createObjectBuilder().add(KEY, ref).build().get(KEY); выглядят немного странно, но это единственный переносимый способ обернуть значение в JsonValue, который я нашел до сих пор.

Фактически в JavaEE8 вы можете заменить его:

Json.createValue()

или ниже:

JsonProvider.provider().createValue()

вызов.