JSON Processing in Java EE 7 – Java EE 7中的JSON处理

最后修改: 2017年 1月 22日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This article will show you how to process JSON using only core Java EE, without the use of third-party dependencies like Jersey or Jackson. Pretty much everything we’ll be using is provided by the javax.json package.

本文将向您展示如何仅使用核心Java EE来处理JSON,而不使用Jersey或Jackson等第三方依赖项。我们将使用的几乎所有东西都由javax.json包提供。

2. Writing an Object to JSON String

2.将对象写成JSONString

Converting a Java object into a JSON String is super easy. Let’s assume we have a simple Person class:

将一个Java对象转换为JSON String是非常容易的。让我们假设我们有一个简单的Person类。

public class Person {
    private String firstName;
    private String lastName;
    private Date birthdate;

    // getters and setters
}

To convert an instance of that class to a JSON String, first we need to create an instance of JsonObjectBuilder and add property/value pairs using the add() method:

要将该类的一个实例转换为JSON String,首先我们需要创建一个JsonObjectBuilder的实例,并使用add()方法添加属性/值对。

JsonObjectBuilder objectBuilder = Json.createObjectBuilder()
  .add("firstName", person.getFirstName())
  .add("lastName", person.getLastName())
  .add("birthdate", new SimpleDateFormat("DD/MM/YYYY")
  .format(person.getBirthdate()));

Notice that the add() method has a few overloaded versions. It can receive most of the primitive types (as well as boxed objects) as its second parameter.

注意,add()方法有几个重载版本。它可以接收大多数原始类型(以及盒式对象)作为其第二个参数。

Once we’re done setting the properties we just need to write the object into a String:

一旦我们完成了属性的设置,我们只需要把对象写成一个String

JsonObject jsonObject = objectBuilder.build();
        
String jsonString;
try(Writer writer = new StringWriter()) {
    Json.createWriter(writer).write(jsonObject);
    jsonString = writer.toString();
}

And that’s it! The generated String will look like this:

这就是了!生成的String将看起来像这样。

{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978"}

2.1. Using JsonArrayBuilder to Build Arrays

2.1.使用JsonArrayBuilder来构建数组

Now, to add a little more complexity to our example, let’s assume that the Person class was modified to add a new property called emails which will contain a list of email addresses:

现在,为了给我们的例子增加一点复杂性,让我们假设Person 类被修改以添加一个名为emails 的新属性,它将包含一个电子邮件地址的列表。

public class Person {
    private String firstName;
    private String lastName;
    private Date birthdate;
    private List<String> emails;
    
    // getters and setters

}

To add all the values from that list to the JsonObjectBuilder we’ll need the help of JsonArrayBuilder:

为了将该列表中的所有值添加到JsonObjectBuilder中,我们将需要JsonArrayBuilder的帮助。

JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
                
for(String email : person.getEmails()) {
    arrayBuilder.add(email);
}
        
objectBuilder.add("emails", arrayBuilder);

Notice that we’re using yet another overloaded version of the add() method that takes a JsonArrayBuilder object as its second parameter.

请注意,我们使用的是另一个重载版本的add()方法,它需要一个JsonArrayBuilder对象作为其第二个参数。

So, let’s look at the generated String for a Person object with two email addresses:

所以,让我们看看一个有两个电子邮件地址的Person对象的生成的String。

{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978",
 "emails":["michael.scott@dd.com","michael.scarn@gmail.com"]}

2.2. Formatting the Output With PRETTY_PRINTING

2.2.用PRETTY_PRINTING格式化输出

So we have successfully converted a Java object to a valid JSON String. Now, before moving to the next section, let’s add some simple formatting to make the output more “JSON-like” and easier to read.

所以我们已经成功地将一个Java对象转换为一个有效的JSON String。现在,在进入下一节之前,让我们添加一些简单的格式化,使输出更 “像JSON”,更容易阅读。

In the previous examples, we created a JsonWriter using the straightforward Json.createWriter() static method. In order to get more control of the generated String, we will leverage Java 7’s JsonWriterFactory ability to create a writer with a specific configuration.

在前面的例子中,我们使用直接的Json.createWriter()静态方法创建了一个JsonWriter。为了对生成的String进行更多的控制,我们将利用Java 7的JsonWriterFactory能力来创建一个具有特定配置的写入器。

Map<String, Boolean> config = new HashMap<>();

config.put(JsonGenerator.PRETTY_PRINTING, true);
        
JsonWriterFactory writerFactory = Json.createWriterFactory(config);
        
String jsonString;
 
try(Writer writer = new StringWriter()) {
    writerFactory.createWriter(writer).write(jsonObject);
    jsonString = writer.toString();
}

The code may look a bit verbose, but it really doesn’t do much.

这段代码可能看起来有点冗长,但实际上并没有什么作用。

First, it creates an instance of JsonWriterFactory passing a configuration map to its constructor. The map contains only one entry which sets true to the PRETTY_PRINTING property. Then, we use that factory instance to create a writer, instead of using Json.createWriter().

首先,它创建了一个JsonWriterFactory的实例,向其构造函数传递一个配置图。该地图只包含一个条目,即对PRETTY_PRINTING属性设置为 “true”。然后,我们使用该工厂实例来创建一个写作者,而不是使用Json.createWriter()

The new output will contain the distinctive line breaks and tabulation that characterizes a JSON String:

新的输出将包含独特的换行符和制表符,这是JSON String的特点。

{
    "firstName":"Michael",
    "lastName":"Scott",
    "birthdate":"06/15/1978",
    "emails":[
        "michael.scott@dd.com",
        "michael.scarn@gmail.com"
    ]
}

3. Building a Java Object From a String

3.从一个字符串构建一个Java对象

Now let’s do the opposite operation: convert a JSON String into a Java object.

现在让我们做一个相反的操作:将JSON String转换成一个Java对象。

The main part of the conversion process revolves around JsonObject. To create an instance of this class, use the static method Json.createReader() followed by readObject():

转换过程的主要部分围绕着JsonObject。要创建这个类的实例,请使用静态方法Json.createReader(),然后是readObject()

JsonReader reader = Json.createReader(new StringReader(jsonString));

JsonObject jsonObject = reader.readObject();

The createReader() method takes an InputStream as a parameter. In this example, we’re using a StringReader, since our JSON is contained in a String object, but this same method could be used to read content from a file, for example, using FileInputStream.

createReader()方法需要一个InputStream作为参数。在这个例子中,我们使用一个StringReader,因为我们的JSON包含在一个String对象中,但是这个方法也可以用来从一个文件中读取内容,例如,使用FileInputStream

With an instance of JsonObject at hand, we can read the properties using the getString() method and assign the obtained values to a newly created instance of our Person class:

有了JsonObject的实例,我们可以使用getString() 方法读取属性,并将获得的值分配给新创建的Person 类实例。

Person person = new Person();

person.setFirstName(jsonObject.getString("firstName"));
person.setLastName(jsonObject.getString("lastName"));
person.setBirthdate(dateFormat.parse(jsonObject.getString("birthdate")));

3.1. Using JsonArray to Get List Values

3.1.使用JsonArray来获取List

We’ll need to use a special class, called JsonArray to extract list values from JsonObject:

我们需要使用一个特殊的类,叫做JsonArray来从JsonObject中提取列表值。

JsonArray emailsJson = jsonObject.getJsonArray("emails");

List<String> emails = new ArrayList<>();

for (JsonString j : emailsJson.getValuesAs(JsonString.class)) {
    emails.add(j.getString());
}

person.setEmails(emails);

That’s it! We have created a complete instance of Person from a Json String.

这就是了!我们已经从一个Json String中创建了一个完整的Person的实例。

4. Querying for Values

4.查询数值

Now, let’s assume we are interested in a very specific piece of data that lies inside a JSON String.

现在,让我们假设我们对位于JSON String内的一个非常具体的数据感兴趣。

Consider the JSON below representing a client from a pet shop. Let’s say that, for some reason, you need to get the name of the third pet from the pets list:

考虑下面这个代表一个宠物店客户的JSON。假设由于某种原因,你需要从宠物列表中获得第三只宠物的名字。

{
    "ownerName": "Robert",
    "pets": [{
        "name": "Kitty",
        "type": "cat"
    }, {
        "name": "Rex",
        "type": "dog"
    }, {
        "name": "Jake",
        "type": "dog"
    }]
}

Converting the whole text into a Java object just to get a single value wouldn’t be very efficient. So, let’s check a couple of strategies to query JSON Strings without having to go through the whole conversion ordeal.

如果只是为了得到一个单一的值而将整个文本转换成一个Java对象,效率就不高了。因此,让我们看看有哪些策略可以查询JSON Strings,而不必经历整个转换过程。

4.1. Querying Using Object Model API

4.1.使用对象模型API进行查询

Querying for a property’s value with a known location in the JSON structure is straightforward. We can use an instance of JsonObject, the same class used in previous examples:

用JSON结构中的已知位置来查询一个属性的值是很直接的。我们可以使用JsonObject,的一个实例,也就是前面例子中使用的类。

JsonReader reader = Json.createReader(new StringReader(jsonString));

JsonObject jsonObject = reader.readObject();

String searchResult = jsonObject
  .getJsonArray("pets")
  .getJsonObject(2)
  .getString("name");

The catch here is to navigate through jsonObject properties using the correct sequence of get*() methods.

这里的问题是使用正确的jsonObject方法顺序浏览get*()属性。

In this example, we first get a reference to the “pets” list using getJsonArray(), which returns a list with 3 records. Then, we use getJsonObject() method, which takes an index as a parameter, returning another JsonObject representing the third item in the list. Finally, we use getString() to get the string value we are looking for.

在这个例子中,我们首先使用getJsonArray()获得对 “pets “列表的引用,它返回一个有3条记录的列表。然后,我们使用getJsonObject()方法,它以一个索引为参数,返回另一个JsonObject,代表列表中的第三个项目。最后,我们使用getString()来获取我们正在寻找的字符串值。

4.2. Querying Using Streaming API

4.2.使用流API进行查询

Another way to perform precise queries on a JSON String is using the Streaming API, which has JsonParser as its main class.

另一种对JSON String进行精确查询的方法是使用Streaming API,它将JsonParser作为其主类。

JsonParser provides extremely fast, read-only, forward access to JS, with the drawback of being somewhat more complicated than the Object Model:

JsonParser提供了对JS的极快的、只读的、向前的访问,缺点是比对象模型要复杂一些。

JsonParser jsonParser = Json.createParser(new StringReader(jsonString));

int count = 0;
String result = null;

while(jsonParser.hasNext()) {
    Event e = jsonParser.next();
    
    if (e == Event.KEY_NAME) {
        if(jsonParser.getString().equals("name")) {
            jsonParser.next();
           
            if(++count == 3) {
                result = jsonParser.getString();
                break;
            }
        }   
    }
}

This example delivers the same result as the previous one. It returns the name from the third pet in the pets list.

这个例子的结果与上一个例子相同。它返回pets列表中第三个宠物的名称

Once a JsonParser is created using Json.createParser(), we need to use an iterator (hence the “forward access” nature of the JsonParser) to navigate through the JSON tokens until we get to the property (or properties) we are looking for.

一旦使用JsonParser创建了Json.createParser(),我们需要使用一个迭代器(因此JsonParser的 “向前访问 “性质)来浏览JSON令牌,直到我们到达我们正在寻找的属性(或属性)。

Every time we step through the iterator we move to the next token of the JSON data. So we have to be careful to check if the current token has the expected type. This is done by checking the Event returned by the next() call.

每当我们通过迭代器时,我们就会移动到JSON数据的下一个标记。所以我们必须小心地检查当前标记是否具有预期的类型。这是通过检查Event返回的next() 调用来完成的。

There are many different types of tokens. In this example, we are interested in the KEY_NAME types, which represent the name of a property (e.g. “ownerName”, “pets”, “name”, “type”). Once we stepped through a KEY_NAME token with a value of “name” for the third time, we know that the next token will contain a string value representing the name of the third pet from the list.

有许多不同类型的令牌。在这个例子中,我们对KEY_NAME类型感兴趣,它代表一个属性的名称(例如 “ownerName”、”pets”、”name”、”type”)。一旦我们第三次踏过一个值为 “name “的KEY_NAME标记,我们知道下一个标记将包含一个字符串值,代表列表中第三个宠物的名字。

This is definitely harder than using the Object Model API, especially for more complicated JSON structures. The choice between one or the other, as always, depends on the specific scenario you will be dealing with.

这肯定比使用对象模型API更难,特别是对于更复杂的JSON结构。一如既往,在两者之间的选择取决于你要处理的具体场景。

5. Conclusion

5.结论

We have covered a lot of ground on the Java EE JSON Processing API with a couple of simple examples. To learn other cool stuff about JSON processing, check our series of Jackson articles.

我们已经通过几个简单的例子介绍了Java EE JSON处理API的很多内容。要了解有关JSON处理的其他很酷的东西,请查看我们的杰克逊系列文章

Check the source code of the classes used in this article, as well as some unit tests, in our GitHub repository.

在我们的GitHub资源库中查看本文中使用的类的源代码以及一些单元测试。