1. 概述
本文將指導您僅使用核心 Java EE,而無需使用第三方依賴項(如 Jersey 或 Jackson)來處理 JSON。 我們將使用的幾乎所有內容都來自 javax.json 包。
2. 將對象轉換為 JSON 字符串
將 Java 對象轉換為 JSON 字符串非常簡單。假設我們有一個簡單的 <em lang="en">Person</em> 類:
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() 方法有幾個重載版本。它可以接收最基本的類型(以及封裝後的對象)作為其第二個參數。
完成設置屬性後,只需將對象寫入一個 String 即可:
JsonObject jsonObject = objectBuilder.build();
String jsonString;
try(Writer writer = new StringWriter()) {
Json.createWriter(writer).write(jsonObject);
jsonString = writer.toString();
}<p>這就是全部!生成的 <em >String</em> 將會是這樣的:</p>
{"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":["[email protected]","[email protected]"]}2.2. 使用 <em>PRETTY_PRINTING</em> 格式化輸出
我們已經成功地將 Java 對象轉換為有效的 JSON String。 在轉向下一部分之前,讓我們添加一些簡單的格式化,使輸出更具“JSON”風格,更易於閲讀。
在之前的示例中,我們使用 JsonWriter 類,通過 Json.<em>createWriter()</em> 靜態方法進行創建。 為了更好地控制生成的 String,我們將利用 Java 7 的 <a href="http://docs.oracle.com/javaee/7/api/javax/json/JsonWriterFactory.html">JsonWriterFactory</a> 機制,創建一個具有特定配置的 JsonWriter。
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 的實例,並將一個配置映射傳遞到其構造函數中。該映射只包含一個條目,將 PRETTY_PRINTING 屬性設置為 true。然後,我們使用該工廠實例創建寫入器,而不是使用 Json.createWriter()。
新的輸出將包含 JSON 字符串中特有的換行符和製表符。
{
"firstName":"Michael",
"lastName":"Scott",
"birthdate":"06/15/1978",
"emails":[
"[email protected]",
"[email protected]"
]
}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 獲取 List 值
我們需要使用一個特殊的類,稱為 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 String 內部。
以下 JSON 代表一個寵物店的客户。出於某種原因,我們需要獲取寵物列表中第三個寵物的名稱:
{
"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");
關鍵在於通過正確順序使用 jsonObject 的屬性進行導航。
在以下示例中,我們首先使用 getJsonArray() 獲取“pets”列表的引用,該方法返回一個包含 3 條記錄的列表。然後,我們使用 getJsonObject() 方法,該方法作為參數接受一個索引,返回另一個 JsonObject,它代表列表中的第三個項目。最後,我們使用 getString() 方法來獲取我們正在查找的字符串值。
4.2. 使用流式 API 查詢
使用流式 API 對 JSON 字符串執行精確查詢的另一種方法,其主要類是 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列表中第三隻寵物返回name。
使用JsonParser創建後,如使用Json.createParser(),我們需要使用迭代器(因此JsonParser具有“前向訪問”特性)來遍歷 JSON 令牌,直到我們找到所需的屬性(或屬性)為止。
每次我們遍歷迭代器,我們都會移動到 JSON 數據中的下一個令牌。因此,我們必須小心地檢查當前令牌是否具有預期類型。這通過檢查Event,由next()調用返回來實現。
有許多不同類型的令牌。在此示例中,我們對KEY_NAME類型感興趣,該類型表示屬性的名稱(例如,“ownerName”、“pets”、“name”、“type”)。當我們經過第三次“name”類型的令牌,並獲得一個值為“name”的值後,我們知道下一個令牌將包含表示列表中的第三隻寵物名稱的字符串值。
與使用對象模型 API 相比,尤其對於更復雜的 JSON 結構,這肯定更困難。選擇使用哪種方法,就像總是這樣一樣,取決於您要處理的特定場景。
5. 結論
我們已經對 Java EE JSON 處理 API 進行了廣泛的介紹,並通過一些簡單的示例進行了説明。要了解更多關於 JSON 處理的有趣內容,請查看我們的 Jackson 系列文章。