Java EE 7中的JSON处理

2025/04/08

1. 概述

本文将向你展示如何仅使用核心Java EE来处理JSON,而无需使用Jersey或Jackson等第三方依赖,我们将使用的几乎所有内容均由javax.json包提供。

2. 将对象写入JSON字符串

将Java对象转换为JSON字符串非常简单,假设我们有一个简单的Person类:

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

    // getters and setters
}

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

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

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

一旦我们完成了属性的设置,我们只需要将对象写入字符串:

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

就这样,生成的字符串将如下所示:

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

2.1 使用JsonArrayBuilder构建数组

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

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

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

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

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

那么,让我们看一下具有两个电子邮件地址的Person对象生成的字符串:

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

2.2 使用PRETTY_PRINTING格式化输出

因此,我们已成功将Java对象转换为有效的JSON字符串。现在,在进入下一部分之前,让我们添加一些简单的格式,使输出更“像JSON”且更易于阅读。

在前面的例子中,我们使用简单的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();
}

代码可能看起来有点冗长,但实际上并没有做太多事情。

首先,它创建JsonWriterFactory的一个实例,并将配置Map传递给其构造函数,该Map仅包含一个条目,该条目将PRETTY_PRINTING属性设置为true。然后,我们使用该工厂实例创建一个写入器,而不是使用Json.createWriter()。

新输出将包含描述JSON字符串的独特换行符和制表符:

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

3. 从字符串构建Java对象

现在我们做相反的操作:将JSON字符串转换为Java对象。

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

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

JsonObject jsonObject = reader.readObject();

createReader()方法以InputStream作为参数,在此示例中,我们使用StringReader,因为我们的JSON包含在String对象中,但此方法也可用于从文件读取内容,例如使用FileInputStream

有了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 使用JsonArray获取列表值

我们需要使用一个名为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);

就这样,我们已经从Json字符串创建了一个完整的Person实例。

4. 查询值

现在,假设我们对JSON字符串内的具体数据非常感兴趣。

考虑下面的表示宠物店的一位客户的JSON,假设出于某种原因,你需要从宠物列表中获取第3只宠物的名称:

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

将整个文本转换为Java对象只是为了获得单个值,这种做法效率不高。因此,让我们来看看几种无需经历整个转换过程即可查询JSON字符串的策略。

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

查询JSON结构中已知位置的属性值非常简单,我们可以使用JsonObject的实例,该类与前面的示例中使用的类相同:

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

JsonObject jsonObject = reader.readObject();

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

这里的关键是使用正确的get()方法序列访问jsonObject属性。

在此示例中,我们首先使用getJsonArray()获取对“pets”列表的引用,该方法返回包含3条记录的列表。然后,我们使用getJsonObject()方法,该方法将索引作为参数,返回另一个代表列表中第3项的JsonObject。最后,我们使用getString()获取我们要查找的字符串值。

4.2 使用流式API进行查询

对JSON字符串执行精确查询的另一种方法是使用流式API,其以JsonParser作为其主类。

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

此示例提供的结果与上一个示例相同,它返回pets列表中第三个宠物的名称。

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

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

有许多不同类型的标记;在此示例中,我们对KEY_NAME类型感兴趣,它表示属性的名称(例如“ownerName”、“pets”、“name”、“type”)。一旦我们第3次遍历值为“name”的KEY_NAME标记,我们就知道下一个标记将包含一个字符串值,表示列表中第3个宠物的名称。

这肯定比使用对象模型API更难,尤其是对于更复杂的JSON结构,两者之间的选择始终取决于你要处理的具体场景。

5. 总结

我们通过几个简单示例介绍了Java EE JSON处理API的大量内容,若要了解有关JSON处理的其他精彩内容,请查看我们的Jackson系列文章

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章