Como construir JSON fluentemente em Java?

107

Estou pensando em algo como:

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

Qual biblioteca Java JSON é a melhor para esse tipo de construção fluente?

Atualização : Enrolei GSON e obtive quase o resultado desejado ... com um obstáculo .

ripper234
fonte
1
Não acho que tenha visto nenhuma biblioteca JSON que segue esse estilo. Talvez você possa estender uma biblioteca existente para fazer o que deseja?
aroth
1
@aroth - Estou escrevendo um wrapper em torno de com.google.gson enquanto conversamos.
ripper234
1
Quase pronto - stackoverflow.com/questions/8876271/…
ripper234
1
Construa o "ninho" apropriado de mapas e listas e serialize-o. Evite declarações de "polímero de cadeia longa".
Hot Licks

Respostas:

141

Estou usando a biblioteca org.json e achei-a agradável e amigável.

Exemplo:

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

RESULTADO:

{"JSON2":"Hello my World!","JSON3":{"key1":"value1"},"JSON1":"Hello World!"}
dku.rajkumar
fonte
15
Isso não é muito fluente.
Vlad,
A web que você forneceu não funciona mais. Você se importaria de atualizá-lo?
Saša Zejnilović
13
@Vlad - isso é exatamente o que os construtores fluentes me sugerem. Você pode fornecer um exemplo de uma configuração que você acha que é melhor?
scubbo
3
Esta ainda é a opção mais limpa. É ridículo que os desenvolvedores Java ainda tenham que fazer uma pausa para descobrir a melhor opção para analisar / construir JSON em 2020.
mtyson
113

Consulte a especificação Java EE 7 Json . Este é o caminho certo:

String json = Json.createObjectBuilder()
            .add("key1", "value1")
            .add("key2", "value2")
            .build()
            .toString();
Yumarx Polanco
fonte
8
Essa deve ser marcada como a resposta correta em 2015, pois faz parte do javax.jsonJava 7 e posterior.
Sridhar Sarnobat
42
É Java EE API, não Java SE.
igorp1024
Alguma ideia de como criar um objeto JsonString a partir de um objeto String na API javax.json?
Raymond
4
dependência de maven: mvnrepository.com/artifact/javax/javaee-api/7.0
ankitkpd
12
A dependência do Maven para o único javax.jsonpacote é esta
JeanValjean
12

Recentemente, criei uma biblioteca para criar objetos Gson com fluência:

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

Funciona assim:

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

E, por meio da magia dos genéricos, ele gera erros de compilação se você tentar adicionar um elemento a uma matriz com uma chave de propriedade ou um elemento a um objeto sem um nome de propriedade:

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, há suporte para mapeamento na API, que permite mapear seus objetos de domínio para JSON. O objetivo é que, quando o Java8 for lançado, você seja capaz de fazer algo assim:

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

Se você está usando o Jackson para JsonNodeconstruir muitas coisas no código, você pode se interessar pelo seguinte conjunto de utilitários. A vantagem de usá-los é que eles suportam um estilo de encadeamento mais natural que mostra melhor a estrutura do JSON em construção.

Aqui está um exemplo de uso:

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

...

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

O que é equivalente ao seguinte JSON:

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

Aqui estão as aulas:

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

  }

}

Observe que a implementação usa o Lombok , mas você pode removê-lo facilmente para preencher o padrão Java.

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

Se você acha que a solução acima é elegante, experimente minha biblioteca JsonBuilder . Ele foi criado para permitir uma maneira de construir estruturas json para muitos tipos de bibliotecas Json. As implementações atuais incluem Gson, Jackson e MongoDB. Para ie. Jackson acabou de trocar:

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

Ficarei feliz em adicionar outros a pedido, também é bastante fácil de implementar um por si mesmo.

Homyk
fonte
infelizmente, não em maven central neste momento, veja github.com/HknL/JsonBuilder/issues/8
dschulten
1

Parece que você provavelmente deseja obter o json-lib:

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

Douglas Crockford é o cara que inventou JSON; sua biblioteca Java está aqui:

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

Parece que o pessoal da json-lib começou de onde Crockford parou. Ambos suportam totalmente JSON, ambos usam (compatível, pelo que eu posso dizer) construções JSONObject, JSONArray e JSONFunction.

'Espero que ajude ..

paulsm4
fonte
Suporta sintaxe fluente?
ripper234
0

é muito mais fácil do que você pensa escrever o seu próprio, basta usar uma interface para JsonElementInterfacecom um método string toJson()e uma classe abstrata AbstractJsonElementimplementando essa interface,

então tudo que você precisa fazer é ter uma classe para JSONPropertyque implemente a interface e JSONValue(qualquer token), JSONArray([...]) e JSONObject({...}) que estenda a classe abstrata

JSONObjecttem uma lista de JSONProperty's
JSONArraytem uma lista de AbstractJsonElement' s

sua função add em cada um deve pegar uma lista vararg desse tipo e retornar this

agora se você não gosta de algo você pode apenas ajustá-lo

o benefício da interface e da classe abstrata é que JSONArraynão pode aceitar propriedades, mas JSONPropertypode aceitar objetos ou matrizes

Austin_Anderson
fonte