Jak płynnie budować JSON w Javie?

107

Myślę o czymś takim:

String json = new JsonBuilder()
  .add("key1", "value1")
  .add("key2", "value2")
  .add("key3", new JsonBuilder()
    .add("innerKey1", "value3"))
  .toJson();

Która biblioteka Java JSON jest najlepsza do tego rodzaju płynnego budowania?

Aktualizacja : owinąłem GSON i uzyskałem prawie pożądany rezultat ... z jednym problemem .

zrywak234
źródło
1
Chyba nie widziałem żadnej biblioteki JSON zgodnej z tym stylem. Może mógłbyś rozszerzyć istniejącą bibliotekę, aby robić to, co chcesz?
aroth
1
@aroth - na bieżąco piszę opakowanie wokół com.google.gson.
ripper234
1
Prawie gotowe - stackoverflow.com/questions/8876271/ ...
ripper234
1
Skonstruuj odpowiednie „gniazdo” map i list i nadaj mu serializację. Unikaj stwierdzeń „polimer o długim łańcuchu”.
Hot Licks

Odpowiedzi:

141

Korzystam z biblioteki org.json i stwierdziłem, że jest miła i przyjazna.

Przykład:

String jsonString = new JSONObject()
                  .put("JSON1", "Hello World!")
                  .put("JSON2", "Hello my World!")
                  .put("JSON3", new JSONObject().put("key1", "value1"))
                  .toString();

System.out.println(jsonString);

WYNIK:

{"JSON2":"Hello my World!","JSON3":{"key1":"value1"},"JSON1":"Hello World!"}
dku.rajkumar
źródło
15
To nie jest zbyt płynne.
Vlad
Podana sieć już nie działa. Czy mógłbyś to zaktualizować?
Saša Zejnilović,
13
@Vlad - dokładnie to sugerują mi biegli budowniczowie. Czy możesz podać przykład konfiguracji, która Twoim zdaniem jest lepsza?
scubbo
3
Jest to nadal czystsza opcja. To śmieszne, że programiści Java wciąż muszą się zatrzymać, aby znaleźć najlepszą opcję dla analizy / kompilacji JSON w 2020 r.
mtyson
113

Zobacz specyfikację Java EE 7 Json . To jest właściwy sposób:

String json = Json.createObjectBuilder()
            .add("key1", "value1")
            .add("key2", "value2")
            .build()
            .toString();
Yumarx Polanco
źródło
8
To powinno być oznaczone jako poprawna odpowiedź w 2015 roku, ponieważ jest to część javax.jsonw Javie 7 i nowszych.
Sridhar Sarnobat
42
Jest to API Java EE, a nie Java SE.
igorp1024
Masz pomysł, jak utworzyć obiekt JsonString z obiektu String w interfejsie API javax.json?
Raymond
4
Zależność maven: mvnrepository.com/artifact/javax/javaee-api/7.0
ankitkpd
12
Zależność Maven dla jedynego javax.jsonpakietu jest taka
JeanValjean
12

Niedawno stworzyłem bibliotekę do płynnego tworzenia obiektów Gson:

http://jglue.org/fluent-json/

Działa to tak:

  JsonObject jsonObject = JsonBuilderFactory.buildObject() //Create a new builder for an object
  .addNull("nullKey")                            //1. Add a null to the object

  .add("stringKey", "Hello")                     //2. Add a string to the object
  .add("stringNullKey", (String) null)           //3. Add a null string to the object

  .add("numberKey", 2)                           //4. Add a number to the object
  .add("numberNullKey", (Float) null)            //5. Add a null number to the object

  .add("booleanKey", true)                       //6. Add a boolean to the object
  .add("booleanNullKey", (Boolean) null)         //7. Add a null boolean to the object

  .add("characterKey", 'c')                      //8. Add a character to the object
  .add("characterNullKey", (Character) null)     //9. Add a null character to the object

  .addObject("objKey")                           //10. Add a nested object
    .add("nestedPropertyKey", 4)                 //11. Add a nested property to the nested object
    .end()                                       //12. End nested object and return to the parent builder

  .addArray("arrayKey")                          //13. Add an array to the object
    .addObject()                                 //14. Add a nested object to the array
      .end()                                     //15. End the nested object
    .add("arrayElement")                         //16. Add a string to the array
    .end()                                       //17. End the array

    .getJson();                                  //Get the JsonObject

String json = jsonObject.toString();

Dzięki magii typów ogólnych generuje błędy kompilacji, jeśli spróbujesz dodać element do tablicy z kluczem właściwości lub element do obiektu bez nazwy właściwości:

JsonObject jsonArray = JsonBuilderFactory.buildArray().addObject().end().add("foo", "bar").getJson(); //Error: tried to add a string with property key to array.
JsonObject jsonObject = JsonBuilderFactory.buildObject().addArray().end().add("foo").getJson(); //Error: tried to add a string without property key to an object.
JsonArray jsonArray = JsonBuilderFactory.buildObject().addArray("foo").getJson(); //Error: tried to assign an object to an array.
JsonObject jsonObject = JsonBuilderFactory.buildArray().addObject().getJson(); //Error: tried to assign an object to an array.

Wreszcie interfejs API obsługuje mapowanie, które umożliwia mapowanie obiektów domeny do formatu JSON. Celem jest to, że po wydaniu Java8 będziesz mógł zrobić coś takiego:

Collection<User> users = ...;
JsonArray jsonArray = JsonBuilderFactory.buildArray(users, { u-> buildObject()
                                                                 .add("userName", u.getName())
                                                                 .add("ageInYears", u.getAge()) })
                                                                 .getJson();
Bryn
źródło
8

Jeśli używasz Jacksona i dużo JsonNodebudujesz w kodzie, możesz być interesujący w następującym zestawie narzędzi. Zaletą ich stosowania jest to, że obsługują bardziej naturalny styl tworzenia łańcuchów, który lepiej pokazuje strukturę tworzonego JSON.

Oto przykład użycia:

import static JsonNodeBuilders.array;
import static JsonNodeBuilders.object;

...

val request = object("x", "1").with("y", array(object("z", "2"))).end();

Co jest równoważne z następującym kodem JSON:

{"x":"1", "y": [{"z": "2"}]}

Oto zajęcia:

import static lombok.AccessLevel.PRIVATE;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;

import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;

/**
 * Convenience {@link JsonNode} builder.
 */
@NoArgsConstructor(access = PRIVATE)
public final class JsonNodeBuilders {

  /**
   * Factory methods for an {@link ObjectNode} builder.
   */

  public static ObjectNodeBuilder object() {
    return object(JsonNodeFactory.instance);
  }

  public static ObjectNodeBuilder object(@NonNull String k1, boolean v1) {
    return object().with(k1, v1);
  }

  public static ObjectNodeBuilder object(@NonNull String k1, int v1) {
    return object().with(k1, v1);
  }

  public static ObjectNodeBuilder object(@NonNull String k1, float v1) {
    return object().with(k1, v1);
  }

  public static ObjectNodeBuilder object(@NonNull String k1, String v1) {
    return object().with(k1, v1);
  }

  public static ObjectNodeBuilder object(@NonNull String k1, String v1, @NonNull String k2, String v2) {
    return object(k1, v1).with(k2, v2);
  }

  public static ObjectNodeBuilder object(@NonNull String k1, String v1, @NonNull String k2, String v2,
      @NonNull String k3, String v3) {
    return object(k1, v1, k2, v2).with(k3, v3);
  }

  public static ObjectNodeBuilder object(@NonNull String k1, JsonNodeBuilder<?> builder) {
    return object().with(k1, builder);
  }

  public static ObjectNodeBuilder object(JsonNodeFactory factory) {
    return new ObjectNodeBuilder(factory);
  }

  /**
   * Factory methods for an {@link ArrayNode} builder.
   */

  public static ArrayNodeBuilder array() {
    return array(JsonNodeFactory.instance);
  }

  public static ArrayNodeBuilder array(@NonNull boolean... values) {
    return array().with(values);
  }

  public static ArrayNodeBuilder array(@NonNull int... values) {
    return array().with(values);
  }

  public static ArrayNodeBuilder array(@NonNull String... values) {
    return array().with(values);
  }

  public static ArrayNodeBuilder array(@NonNull JsonNodeBuilder<?>... builders) {
    return array().with(builders);
  }

  public static ArrayNodeBuilder array(JsonNodeFactory factory) {
    return new ArrayNodeBuilder(factory);
  }

  public interface JsonNodeBuilder<T extends JsonNode> {

    /**
     * Construct and return the {@link JsonNode} instance.
     */
    T end();

  }

  @RequiredArgsConstructor
  private static abstract class AbstractNodeBuilder<T extends JsonNode> implements JsonNodeBuilder<T> {

    /**
     * The source of values.
     */
    @NonNull
    protected final JsonNodeFactory factory;

    /**
     * The value under construction.
     */
    @NonNull
    protected final T node;

    /**
     * Returns a valid JSON string, so long as {@code POJONode}s not used.
     */
    @Override
    public String toString() {
      return node.toString();
    }

  }

  public final static class ObjectNodeBuilder extends AbstractNodeBuilder<ObjectNode> {

    private ObjectNodeBuilder(JsonNodeFactory factory) {
      super(factory, factory.objectNode());
    }

    public ObjectNodeBuilder withNull(@NonNull String field) {
      return with(field, factory.nullNode());
    }

    public ObjectNodeBuilder with(@NonNull String field, int value) {
      return with(field, factory.numberNode(value));
    }

    public ObjectNodeBuilder with(@NonNull String field, float value) {
      return with(field, factory.numberNode(value));
    }

    public ObjectNodeBuilder with(@NonNull String field, boolean value) {
      return with(field, factory.booleanNode(value));
    }

    public ObjectNodeBuilder with(@NonNull String field, String value) {
      return with(field, factory.textNode(value));
    }

    public ObjectNodeBuilder with(@NonNull String field, JsonNode value) {
      node.set(field, value);
      return this;
    }

    public ObjectNodeBuilder with(@NonNull String field, @NonNull JsonNodeBuilder<?> builder) {
      return with(field, builder.end());
    }

    public ObjectNodeBuilder withPOJO(@NonNull String field, @NonNull Object pojo) {
      return with(field, factory.pojoNode(pojo));
    }

    @Override
    public ObjectNode end() {
      return node;
    }

  }

  public final static class ArrayNodeBuilder extends AbstractNodeBuilder<ArrayNode> {

    private ArrayNodeBuilder(JsonNodeFactory factory) {
      super(factory, factory.arrayNode());
    }

    public ArrayNodeBuilder with(boolean value) {
      node.add(value);
      return this;
    }

    public ArrayNodeBuilder with(@NonNull boolean... values) {
      for (val value : values)
        with(value);
      return this;
    }

    public ArrayNodeBuilder with(int value) {
      node.add(value);
      return this;
    }

    public ArrayNodeBuilder with(@NonNull int... values) {
      for (val value : values)
        with(value);
      return this;
    }

    public ArrayNodeBuilder with(float value) {
      node.add(value);
      return this;
    }

    public ArrayNodeBuilder with(String value) {
      node.add(value);
      return this;
    }

    public ArrayNodeBuilder with(@NonNull String... values) {
      for (val value : values)
        with(value);
      return this;
    }

    public ArrayNodeBuilder with(@NonNull Iterable<String> values) {
      for (val value : values)
        with(value);
      return this;
    }

    public ArrayNodeBuilder with(JsonNode value) {
      node.add(value);
      return this;
    }

    public ArrayNodeBuilder with(@NonNull JsonNode... values) {
      for (val value : values)
        with(value);
      return this;
    }

    public ArrayNodeBuilder with(JsonNodeBuilder<?> value) {
      return with(value.end());
    }

    public ArrayNodeBuilder with(@NonNull JsonNodeBuilder<?>... builders) {
      for (val builder : builders)
        with(builder);
      return this;
    }

    @Override
    public ArrayNode end() {
      return node;
    }

  }

}

Zauważ, że implementacja używa Lombok , ale możesz łatwo usunąć cukier, aby wypełnić standardowy szablon Java.

btiernay
źródło
2
String json = new JsonBuilder(new GsonAdapter())
  .object("key1", "value1")
  .object("key2", "value2")
  .object("key3")
    .object("innerKey1", "value3")
    .build().toString();

Jeśli uważasz, że powyższe rozwiązanie jest eleganckie, wypróbuj moją bibliotekę JsonBuilder . Został stworzony, aby umożliwić jeden sposób budowania struktur JSON dla wielu typów bibliotek Json. Aktualne wdrożenia obejmują Gson, Jackson i MongoDB. Np. Jackson właśnie się zamienił:

String json = new JsonBuilder(new JacksonAdapter()).

Chętnie dodam inne na życzenie, jest też dość łatwe do wykonania samodzielnie.

Homyk
źródło
niestety nie w tej chwili w Maven
Central
1

Wygląda na to, że prawdopodobnie chcesz zdobyć json-lib:

http://json-lib.sourceforge.net/

Douglas Crockford to facet, który wynalazł JSON; jego biblioteka Java jest tutaj:

http://www.json.org/java/

Wygląda na to, że ludzie z json-lib podjęli się tego, co przerwał Crockford. Oba w pełni obsługują JSON, oba używają (kompatybilnych, o ile wiem) konstrukcji JSONObject, JSONArray i JSONFunction.

„Mam nadzieję, że to pomoże ...

paulsm4
źródło
Czy obsługuje płynną składnię?
ripper234
0

pisanie własnego jest dużo łatwiejsze niż myślisz, po prostu użyj interfejsu JsonElementInterfacez metodą string toJson()i abstrakcyjnej klasy AbstractJsonElementimplementującej ten interfejs,

wtedy wszystko, co musisz zrobić, to mieć klasę JSONPropertyimplementującą interfejs i JSONValue(dowolny token), JSONArray([...]) i JSONObject({...}) rozszerzające klasę abstrakcyjną

JSONObjectma listę JSONProperty's
JSONArrayma listę AbstractJsonElement' s

Twoja funkcja add w każdym z nich powinna pobierać listę vararg tego typu i zwracać this

teraz, jeśli coś ci się nie podoba, możesz to po prostu poprawić

Zaletą interfejsu i klasy abstrakcyjnej jest to, że JSONArraynie może przyjmować właściwości, ale JSONPropertymoże akceptować obiekty lub tablice

Austin_Anderson
źródło