Как свободно создавать JSON на Java?

107

Я думаю о чем-то вроде:

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

Какая библиотека Java JSON лучше всего подходит для такого плавного построения?

Обновление : завернул GSON и получил почти желаемый результат ... с одной заминкой .

потрошитель234
источник
1
Я не думаю, что видел какую-либо библиотеку JSON, которая следует этому стилю. Возможно, вы могли бы расширить существующую библиотеку, чтобы делать то, что вы хотите?
aroth
1
@aroth - я пишу обертку вокруг com.google.gson, пока мы говорим.
ripper234
1
Почти готово - stackoverflow.com/questions/8876271/…
ripper234
1
Создайте соответствующее «гнездо» из карт и списков и сериализуйте его. Избегайте заявлений о «длинноцепочечном полимере».
Hot Licks

Ответы:

143

Я использую библиотеку org.json и считаю ее приятной и дружелюбной.

Пример:

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

ВЫВОД:

{"JSON2":"Hello my World!","JSON3":{"key1":"value1"},"JSON1":"Hello World!"}
dku.rajkumar
источник
15
Это не очень хорошо.
Влад
Предоставленный вами Интернет больше не работает. Не могли бы вы его обновить?
Saša Zejnilović 03
13
@Vlad - именно это мне подсказывают беглые строители. Можете ли вы привести пример установки, которая, по вашему мнению, лучше?
scubbo
4
Это все еще более чистый вариант. Смешно, что разработчикам Java все еще приходится делать паузу, чтобы определить лучший вариант для синтаксического анализа / сборки JSON в 2020 году.
mtyson
113

См. Спецификацию Java EE 7 Json . Это правильный путь:

String json = Json.createObjectBuilder()
            .add("key1", "value1")
            .add("key2", "value2")
            .build()
            .toString();
Юмаркс Поланко
источник
8
Это должно быть отмечено правильным ответом в 2015 году, поскольку он является частью javax.jsonJava 7 и более поздних версий.
Шридхар Сарнобат
43
Это Java EE API, а не Java SE.
igorp1024
4
Зависимость от maven: mvnrepository.com/artifact/javax/javaee-api/7.0
ankitkpd
12
Maven зависимость для единственного javax.jsonпакета это один
Жан Вальжан
1
обратите внимание, что он создает неизменяемый объект
Киран Кумар
12

Недавно я создал библиотеку для свободного создания объектов Gson:

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

Это работает так:

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

И благодаря магии дженериков он генерирует ошибки компиляции, если вы пытаетесь добавить элемент в массив с ключом свойства или элемент к объекту без имени свойства:

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.

Наконец, в API есть поддержка сопоставления, которая позволяет сопоставлять объекты вашего домена с JSON. Цель состоит в том, что после выпуска Java8 вы сможете сделать что-то вроде этого:

Collection<User> users = ...;
JsonArray jsonArray = JsonBuilderFactory.buildArray(users, { u-> buildObject()
                                                                 .add("userName", u.getName())
                                                                 .add("ageInYears", u.getAge()) })
                                                                 .getJson();
Брин
источник
8

Если вы используете Jackson и много JsonNodeстроите код, вам может быть интересен следующий набор утилит. Преимущество их использования заключается в том, что они поддерживают более естественный стиль цепочки, который лучше показывает структуру строящегося JSON.

Вот пример использования:

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

...

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

Это эквивалентно следующему JSON:

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

Вот классы:

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

  }

}

Обратите внимание, что реализация использует Lombok , но вы можете легко удалить сахар, чтобы заполнить шаблон Java.

Btiernay
источник
2
String json = new JsonBuilder(new GsonAdapter())
  .object("key1", "value1")
  .object("key2", "value2")
  .object("key3")
    .object("innerKey1", "value3")
    .build().toString();

Если вы думаете, что вышеприведенное решение элегантно, попробуйте мою JsonBuilder lib. Он был создан, чтобы обеспечить один способ построения структур json для многих типов библиотек Json. Текущие реализации включают Gson, Jackson и MongoDB. Для т.е. Джексон просто поменялся местами:

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

Я с радостью добавлю другие по запросу, также довольно легко реализовать один самостоятельно.

Хомик
источник
к сожалению, сейчас не в maven central, см. github.com/HknL/JsonBuilder/issues/8
dschulten
1

Похоже, вы, вероятно, захотите получить json-lib:

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

Дуглас Крокфорд - парень, который изобрел JSON; его библиотека Java находится здесь:

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

Похоже, люди из json-lib начали с того места, где остановился Крокфорд. Оба полностью поддерживают JSON, оба используют (совместимые, насколько я могу судить) конструкции JSONObject, JSONArray и JSONFunction.

'Надеюсь, это поможет ..

paulsm4
источник
Поддерживает ли он свободный синтаксис?
ripper234
0

гораздо проще, чем вы думаете, написать свой собственный, просто используйте интерфейс для JsonElementInterfaceметода string toJson()и абстрактный класс, AbstractJsonElementреализующий этот интерфейс,

тогда все, что вам нужно сделать, это иметь класс JSONProperty, реализующий интерфейс, и JSONValue(любой токен), JSONArray([...]) и JSONObject({...}), которые расширяют абстрактный класс

JSONObjectимеет список JSONProperty«s
JSONArrayесть список AbstractJsonElement» с

ваша функция добавления в каждом должна принимать список vararg этого типа и возвращать this

теперь, если вам что-то не нравится, вы можете просто настроить это

Преимущество интерфейса и абстрактного класса в том, что он JSONArrayне может принимать свойства, но JSONPropertyможет принимать объекты или массивы

Остин Андерсон
источник
0

Вы можете использовать один из шаблонов Java. Мне нравится этот метод, потому что вы отделяете свою логику от представления.

Java 8+:

<dependency>
  <groupId>com.github.spullara.mustache.java</groupId>
  <artifactId>compiler</artifactId>
  <version>0.9.6</version>
</dependency>

Java 6/7:

<dependency>
  <groupId>com.github.spullara.mustache.java</groupId>
  <artifactId>compiler</artifactId>
  <version>0.8.18</version>
</dependency>

Пример файла шаблона:

{{#items}}
Name: {{name}}
Price: {{price}}
  {{#features}}
  Feature: {{description}}
  {{/features}}
{{/items}}

Может быть задействован какой-то вспомогательный код:

public class Context {
  List<Item> items() {
    return Arrays.asList(
      new Item("Item 1", "$19.99", Arrays.asList(new Feature("New!"), new Feature("Awesome!"))),
      new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly.")))
    );
  }

  static class Item {
    Item(String name, String price, List<Feature> features) {
      this.name = name;
      this.price = price;
      this.features = features;
    }
    String name, price;
    List<Feature> features;
  }

  static class Feature {
    Feature(String description) {
       this.description = description;
    }
    String description;
  }
}

И приведет к:

Name: Item 1
Price: $19.99
  Feature: New!
  Feature: Awesome!
Name: Item 2
Price: $29.99
  Feature: Old.
  Feature: Ugly.
Александр Илюшкин
источник