¿Cómo construir JSON con fluidez en Java?

106

Estoy pensando en algo como:

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

¿Qué biblioteca JSON de Java es mejor para este tipo de construcción fluida?

Actualización : Envolví GSON y obtuve casi el resultado deseado ... con un solo problema .

destripador234
fuente
1
No creo que haya visto ninguna biblioteca JSON que siga ese estilo. ¿Quizás podría ampliar una biblioteca existente para hacer lo que quiera?
aroth
1
@aroth: estoy escribiendo un resumen sobre com.google.gson mientras hablamos.
ripper234
1
Casi listo - stackoverflow.com/questions/8876271/…
ripper234
1
Construya el "nido" apropiado de mapas y listas y serialícelo. Evite las declaraciones de "polímero de cadena larga".
Hot Licks

Respuestas:

140

Estoy usando la biblioteca org.json y me pareció agradable y amigable.

Ejemplo:

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

SALIDA:

{"JSON2":"Hello my World!","JSON3":{"key1":"value1"},"JSON1":"Hello World!"}
dku.rajkumar
fuente
15
Esto no es muy fluido.
Vlad
La web que proporcionaste ya no funciona. ¿Te importaría actualizarlo?
Saša Zejnilović
13
@Vlad: esto es exactamente lo que me sugieren los constructores con fluidez. ¿Puede proporcionar un ejemplo de una configuración que cree que es mejor?
scubbo
3
Esta sigue siendo la opción más limpia. Es ridículo que los desarrolladores de Java todavía tengan que hacer una pausa para encontrar la mejor opción para analizar / compilar JSON en 2020.
mtyson
112

Consulte la especificación Java EE 7 Json . Esta es la manera correcta:

String json = Json.createObjectBuilder()
            .add("key1", "value1")
            .add("key2", "value2")
            .build()
            .toString();
Yumarx Polanco
fuente
8
Esto debe marcarse como la respuesta correcta en 2015, ya que es parte de javax.jsonJava 7 y versiones posteriores.
Sridhar Sarnobat
42
Es la API de Java EE, no Java SE.
igorp1024
¿Alguna idea de cómo crear un objeto JsonString a partir de un objeto String en la API javax.json?
Raymond
3
Dependencia de Maven
ankitkpd
12
La dependencia de Maven para el javax.jsonpaquete único es este
JeanValjean
12

Recientemente creé una biblioteca para crear objetos Gson con fluidez:

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

Funciona así:

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

Y a través de la magia de los genéricos, genera errores de compilación si intenta agregar un elemento a una matriz con una clave de propiedad o un elemento a un objeto sin un nombre de propiedad:

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.

Por último, hay soporte de mapeo en la API que le permite mapear sus objetos de dominio a JSON. El objetivo es que cuando se lance Java8 podrás hacer algo como esto:

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

Si está utilizando Jackson para hacer muchas JsonNodeconstrucciones en código, puede ser interesante el siguiente conjunto de utilidades. El beneficio de usarlos es que admiten un estilo de encadenamiento más natural que muestra mejor la estructura del JSON en construcción.

Aquí hay un ejemplo de uso:

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

...

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

Que es equivalente al siguiente JSON:

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

Aquí están las clases:

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

  }

}

Tenga en cuenta que la implementación usa Lombok , pero puede desugarlo fácilmente para completar el texto estándar de Java.

btiernay
fuente
2
String json = new JsonBuilder(new GsonAdapter())
  .object("key1", "value1")
  .object("key2", "value2")
  .object("key3")
    .object("innerKey1", "value3")
    .build().toString();

Si cree que la solución anterior es elegante, pruebe mi lib JsonBuilder . Fue creado para permitir una forma de construir estructuras json para muchos tipos de bibliotecas Json. Las implementaciones actuales incluyen Gson, Jackson y MongoDB. Por ejemplo. Jackson acaba de cambiar:

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

Con mucho gusto agregaré otros a pedido, también es bastante fácil implementar uno solo.

Homyk
fuente
Desafortunadamente, no en Maven Central en este momento, consulte github.com/HknL/JsonBuilder/issues/8
dschulten
1

Parece que probablemente quieras ponerte en contacto con json-lib:

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

Douglas Crockford es el tipo que inventó JSON; su biblioteca de Java está aquí:

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

Parece que la gente de json-lib continuó donde lo dejó Crockford. Ambos son totalmente compatibles con JSON, ambos usan (compatible, por lo que puedo decir) construcciones JSONObject, JSONArray y JSONFunction.

'Espero que ayude ..

paulsm4
fuente
¿Admite una sintaxis fluida?
ripper234
0

es mucho más fácil de lo que piensa escribir el suyo, solo use una interfaz para JsonElementInterfacecon un método string toJson()y una clase abstracta AbstractJsonElementque implemente esa interfaz,

entonces todo lo que tienes que hacer es tener una clase para JSONPropertyque implemente la interfaz y JSONValue(cualquier token), JSONArray([...]) y JSONObject({...}) que extiendan la clase abstracta

JSONObjecttiene una lista de JSONProperty's
JSONArraytiene una lista de AbstractJsonElement' s

su función de adición en cada uno debe tomar una lista de vararg de ese tipo, y devolver this

ahora, si no te gusta algo, puedes modificarlo

el beneficio de la interfaz y la clase abstracta es que JSONArrayno puede aceptar propiedades, pero JSONPropertypuede aceptar objetos o matrices

Austin_Anderson
fuente