【问题标题】:JSR-353 How to add null values using javax.json.JsonObjectBuilderJSR-353 如何使用 javax.json.JsonObjectBuilder 添加空值
【发布时间】:2014-03-12 21:11:53
【问题描述】:

由于 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。如果客户没有注册年龄,那么上面的代码将抛出 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 样板)或者我错过了什么?

我批评 JSR-353 api 的主要观点是,尽管它看起来像一个非常好的 fluent api(参见 apidoc 的顶级示例),但实际上它不是。

【问题讨论】:

  • 实际上有几十个比 JSR-353 更好的选择;所以也许看看,比如说,Jackson jr (github.com/FasterXML/jackson-jr)?它有真正流畅的 API(“composers”),不像 JSON-P 有更多的“sorta-kinda-almost”版本。

标签: java json jakarta-ee glassfish jsr-353


【解决方案1】:

从工厂获取 JsonObjectBuilder 实现可能很困难,但您可以使其更简单。

创建一个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(...

【讨论】:

  • 有趣的想法。我想知道是否有可能进一步推动这一点,让工厂自己返回 NullAwareJsonObjectBuilder?
  • 我看过使用 ServiceLoader 提供我自己的 JsonProviderImpl 但扩展 glassfish 类并不是一个真正的选择。几乎所有方便的字段和方法都是私有的,因此最终会复制所有内容。
  • 刚刚实施了这个解决方案......效果很好。我在实现方法的过程中学到了一些小知识。首先,您需要一个返回类型(在大多数情况下返回此类型;在 build() 情况下返回 builder.build())。其次,其中带有 JsonValue.NULL 的三元运算符不适用于其他方法签名。我最终得到了一个简单的条件 if (arg1==null) { builder.addNull(arg0); } 其他 { builder.add(arg0,arg1); }
  • 我觉得这太令人难以置信了,这样的事情并不是默认实现的一部分。这有什么好的理由吗?
【解决方案2】:

另一种可能的解决方案是使用简单而精益的助手将值包装到 JsonValue 对象中,如果值为 null,则返回 JsonValue.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()

打电话。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-08
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    • 2016-12-24
    • 1970-01-01
    相关资源
    最近更新 更多