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系列文章。
Post Directory
