如何在 Java 中流畅地构建 JSON?

我想的是:

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

哪个 JavaJSON 库最适合这种流畅的构建?

更新 : 我包装了 GSON 并得到了几乎想要的结果... 只有一个问题

269903 次浏览

听起来你可能想联系一下 json-lib:

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

道格拉斯·克罗克福特是发明 JSON 的家伙,他的 Java 库在这里:

Http://www.json.org/java/

听起来 json-lib 的人接手了 Crocford 的工作。两者都完全支持 JSON,都使用(据我所知是兼容的) JSONObject、 JSONArray 和 JSONfunction 构造。

希望能有所帮助。

我正在使用的 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!"}

我最近创建了一个用于流畅创建 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();

参考实现包含一个流畅的接口

看看 JavaEE7Json 规范。 这是正确的做法:

String json = Json.createObjectBuilder()
.add("key1", "value1")
.add("key2", "value2")
.build()
.toString();
String json = new JsonBuilder(new GsonAdapter())
.object("key1", "value1")
.object("key2", "value2")
.object("key3")
.object("innerKey1", "value3")
.build().toString();

如果您认为上述解决方案是优雅的,那么请尝试我的 JsonBuilder库。它的创建是为了允许以一种方式为许多类型的 Json 库构建 Json 结构。当前的实现包括 Gson、 Jackson 和 MongoDB。因为。杰克逊只是交换:

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

我很乐意根据要求添加其他的,自己实现一个也很容易。

如果您正在使用 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;
}


}


}

注意,该实现使用 龙目岛,但是您可以轻松地将其去糖化以填充 Java 样板。

编写自己的接口比您想象的要容易得多,只需使用带有方法 string toJson()JsonElementInterface接口和实现该接口的抽象类 AbstractJsonElement,

那么您所要做的就是为实现接口的 JSONProperty创建一个类,以及为扩展抽象类的 JSONValue(任何标记)、 JSONArray([ ... ])和 JSONObject({ ... })创建一个类

JSONObject有一份 JSONProperty的名单
JSONArray有一份 AbstractJsonElement的名单

每个函数中的 add 函数都应该采用该类型的 vararg 列表,并返回 this

现在如果你不喜欢什么,你可以调整一下

接口和抽象类的好处是,JSONArray不能接受属性,但是 JSONProperty可以接受对象或数组

您可以使用 Java 模板引擎之一。 我喜欢这种方法,因为它将逻辑与视图分离开来。

Java8 + :

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

下划线-java 库有 json 构建器。

import com.github.underscore.U;


public static void main(String[] args) {
String json = U.objectBuilder()
.add("key1", "value1")
.add("key2", "value2")
.add("key3", U.objectBuilder()
.add("innerKey1", "value3"))
.toJson();
System.out.println(json);
}


Output:
{
"key1": "value1",
"key2": "value2",
"key3": {
"innerKey1": "value3"
}
}

我来这里是为了找到一种用流畅的 json 构建器编写静止端点测试的好方法。在我的例子中,我使用 JSONObject 来构造一个专用的构建器。它需要一些仪器,但是用途非常好:

import lombok.SneakyThrows;
import org.json.JSONObject;


public class MemberJson extends JSONObject {


@SneakyThrows
public static MemberJson builder() {
return new MemberJson();
}


@SneakyThrows
public MemberJson name(String name) {
put("name", name);
return this;
}


}
MemberJson.builder().name("Member").toString();

我使用 org.json 按需创建 json。我的要求是,我有一个基本的 json 对象,但是在将它发送到 API 之前,我需要将它包装在一个包装器元素中。

下面是为函数测试生成这个 json 的代码。

假设下面是 base-request. json 文件的内容

{
"name":"sanjay"
"age":32,
"occupation":"software engineer"
}




String baseRequest = Files.readString(Paths.get("./src/test/resources", "base-request.json"));
JSONObject baseJsonObject = new JSONObject(baseRequest);
JSONObject wrappedJsonObject = new JSONObject().put("employee", baseJsonObject);

现在,wrappedJsonObject 被发送到我的 API,它期望请求的格式是

{
"employee": {
"name":"sanjay"
"age":32,
"occupation":"software engineer"
}
}

下面是测试代码,使用 RestAssureAPI 将 Json 传递给我的 API

RequestSpecification request = given()
.contentType(JSON)
.body(wrappedJsonObject.toString());
response = request.post(new URL(HTTP, host, port, endPoint));