2014-03-12 5 views
11

hinzufügen AS die javax.json docs den Weg legen eine JsonObject wird mit den mitgelieferten Builder wie zu erstellen:JSR-353 Wie Nullwerte mit javax.json.JsonObjectBuilder

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

In diesem Beispiel werden Werte für die gibt Schlüssel.

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

Die JsonOBjectBuilder eine NPE falls Schlüssel werfen oder Wert ist null: Im wirklichen Leben sind die Werte wahrscheinlich von einigen (pojo) Domain-Objekt wie abgeleitet. Falls der Kunde kein registriertes Alter hat, wird der obige Code eine NPE werfen.

Also grundsätzlich für jedes Feld, das ich hinzufügen, muss ich überprüfen, ob der Wert null ist oder nicht, und fügen Sie den tatsächlichen Wert und ansonsten nicht das Feld hinzufügen oder hinzufügen JsonValue.NULL für den Schlüssel. Diese viel (unerwünschtes) vorformulierten verursacht ...

In meinem Fall kam ich wie mit benutzerdefinierten JsonUtils Klasse einschließlich verschiedene statische Methoden auf:

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

und dann ruft:

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

Aber irgendwie, wenn sich nicht richtig anfühlt. Warum bietet die javax.json keine einfache Möglichkeit, Nullwerte hinzuzufügen (ohne wenn sonst Boilerplate) oder fehlt mir etwas?

Mein Hauptpunkt critism gegen den JSR-353 api ist, dass obwohl es wie ein wirklich schön fließend api (siehe oben Beispiel von apidoc) aussieht, aber in Wirklichkeit ist es nicht.

+1

Es gibt buchstäblich Dutzende von besseren Möglichkeiten als JSR-353; also vielleicht einen Blick auf sagen, Jackson jr (https://github.com/FasterXML/jackson-jr)? Es hat wirklich fließende API ("Komponisten"), im Gegensatz zu JSON-P, das mehr "sorta-fast-fast" Version hat. – StaxMan

Antwort

11

Es kann schwierig sein, Ihre JsonObjectBuilder Implementierung von der Fabrik zu bekommen, aber Sie können es einfacher machen.

erstellen JsonObjectBuilder Dekorateur-Klasse, die für null prüft:

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. 
    .. 
} 

Und wie man es benutzt:

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

Interessante Idee. Ich frage mich, ob es möglich ist, dies sogar noch weiter voranzutreiben und die Factory einen NullAwareJsonObjectBuilder selbst zurückgeben zu lassen? –

+0

Ich habe versucht, meinen eigenen JsonProviderImpl mit ServiceLoader bereitzustellen, aber die Erweiterung von Glassfish-Klassen ist nicht wirklich eine Option. Fast alle praktikablen Felder und Methoden sind privat und würden am Ende alles kopieren. –

+1

Nur implementiert diese Lösung ... funktioniert super. Ein paar kleinere Punkte habe ich auf dem Weg zu den implementierten Methoden gelernt. Zuerst benötigen Sie einen Rückgabetyp (geben Sie dies in den meisten Fällen zurück; return builder.build() für den Fall build()). Zweitens funktioniert der ternäre Operator mit JsonValue.NULL nicht für andere Methodensignaturen. Ich endete mit einer einfachen Bedingung if (arg1 == null) {builder.addNull (arg0); } else {builder.add (arg0, arg1); } –

1

Eine weitere mögliche Lösung ist einfach und schlank Helfer zu verwenden, um einen Wert in einzuwickeln JsonValue object oder return JsonValue.NULL, wenn der Wert null ist. Hier

ein Beispiel:

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

Die Verwendung ist einfach:

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

Konstrukte wie Json.createObjectBuilder().add(KEY, ref).build().get(KEY); ein wenig seltsam aussieht, aber es ist die einzige tragbare Art und Weise Wert zu wickeln in JsonValue Ich habe soweit gefunden.

In der Tat in JavaEE8 können Sie es mit ersetzen:

Json.createValue() 

oder Basiswert:

JsonProvider.provider().createValue() 

Anruf.