Jackson JSON Java パーサー API の例についてのチュートリアル
Jackson JSON Java パーサーは非常に人気があり、Springフレームワークでも使用されています。しかし、Java JSON Processing APIはあまり使いやすくなく、JsonからJavaオブジェクトへの自動変換やその逆の機能を提供していません。幸いなことに、JSON処理に使用できる代替APIがいくつかあります。前の記事ではGoogleのGson APIについて学び、それがどれだけ簡単に使用できるかを見ました。
Jackson JSON Java Parserを日本語で言い換えると、「ジャクソンJSON Javaパーサー」となります。
私たちのプロジェクトでJackson JSON Java APIを使用するためには、プロジェクトのビルドパスに追加するか、もしくはMavenを使用している場合は以下の依存関係を追加することができます。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
Jackson-Databindのjarファイルは、jackson-coreおよびjackson-annotationsライブラリに依存しています。そのため、これらのライブラリをビルドパスに直接追加する場合は、必ず3つをすべて追加してください。そうしないと、実行時エラーが発生します。Jackson JSON Parser APIは、JSONをPOJOオブジェクトに簡単に変換する方法を提供し、JSONデータからMapへの簡単な変換もサポートしています。Jacksonはジェネリクスもサポートしており、JSONからオブジェクトへの直接変換も行います。
ジャクソンのJSONサンプル
JSONからPOJO/Javaオブジェクトへの変換の例として、入れ子のオブジェクトと配列を使用した複雑な例を取り上げます。変換には、Javaオブジェクト内の配列、リスト、マップを使用します。以下の構造でemployee.txtというファイルに複雑なJSONが格納されています。
{
"id": 123,
"name": "Pankaj",
"permanent": true,
"address": {
"street": "Albany Dr",
"city": "San Jose",
"zipcode": 95129
},
"phoneNumbers": [
123456,
987654
],
"role": "Manager",
"cities": [
"Los Angeles",
"New York"
],
"properties": {
"age": "29 years",
"salary": "1000 USD"
}
}
私たちには、次のJSONデータに対応するJavaクラスがあります。
package com.scdev.jackson.model;
public class Address {
private String street;
private String city;
private int zipcode;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getZipcode() {
return zipcode;
}
public void setZipcode(int zipcode) {
this.zipcode = zipcode;
}
@Override
public String toString(){
return getStreet() + ", "+getCity()+", "+getZipcode();
}
}
アドレスクラスはルートのJSONデータ内の内部オブジェクトに対応しています。
package com.scdev.jackson.model;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class Employee {
private int id;
private String name;
private boolean permanent;
private Address address;
private long[] phoneNumbers;
private String role;
private List<String> cities;
private Map<String, String> properties;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isPermanent() {
return permanent;
}
public void setPermanent(boolean permanent) {
this.permanent = permanent;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public long[] getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(long[] phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("***** Employee Details *****\n");
sb.append("ID="+getId()+"\n");
sb.append("Name="+getName()+"\n");
sb.append("Permanent="+isPermanent()+"\n");
sb.append("Role="+getRole()+"\n");
sb.append("Phone Numbers="+Arrays.toString(getPhoneNumbers())+"\n");
sb.append("Address="+getAddress()+"\n");
sb.append("Cities="+Arrays.toString(getCities().toArray())+"\n");
sb.append("Properties="+getProperties()+"\n");
sb.append("*****************************");
return sb.toString();
}
public List<String> getCities() {
return cities;
}
public void setCities(List<String> cities) {
this.cities = cities;
}
public Map<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
}
従業員はルートのJSONオブジェクトを表すJava Beanです。さて、Jackson JSONパーサーAPIを使用してJSONをJavaオブジェクトに変換する方法を見てみましょう。
package com.scdev.jackson.json;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.scdev.jackson.model.Address;
import com.scdev.jackson.model.Employee;
public class JacksonObjectMapperExample {
public static void main(String[] args) throws IOException {
//read json file data to String
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//convert json string to object
Employee emp = objectMapper.readValue(jsonData, Employee.class);
System.out.println("Employee Object\n"+emp);
//convert Object to json string
Employee emp1 = createEmployee();
//configure Object mapper for pretty print
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
//writing to console, can write to any output stream such as file
StringWriter stringEmp = new StringWriter();
objectMapper.writeValue(stringEmp, emp1);
System.out.println("Employee JSON is\n"+stringEmp);
}
public static Employee createEmployee() {
Employee emp = new Employee();
emp.setId(100);
emp.setName("David");
emp.setPermanent(false);
emp.setPhoneNumbers(new long[] { 123456, 987654 });
emp.setRole("Manager");
Address add = new Address();
add.setCity("Bangalore");
add.setStreet("BTM 1st Stage");
add.setZipcode(560100);
emp.setAddress(add);
List<String> cities = new ArrayList<String>();
cities.add("Los Angeles");
cities.add("New York");
emp.setCities(cities);
Map<String, String> props = new HashMap<String, String>();
props.put("salary", "1000 Rs");
props.put("age", "28 years");
emp.setProperties(props);
return emp;
}
}
上記のプログラムを実行すると、以下の出力が得られます。
Employee Object
***** Employee Details *****
ID=123
Name=Pankaj
Permanent=true
Role=Manager
Phone Numbers=[123456, 987654]
Address=Albany Dr, San Jose, 95129
Cities=[Los Angeles, New York]
Properties={age=29 years, salary=1000 USD}
*****************************
Employee JSON is
//printing same as above json file data
com.fasterxml.jackson.databind.ObjectMapperはJackson APIで最も重要なクラスであり、JSONをJavaオブジェクトに変換するreadValue()メソッドと、JavaオブジェクトをJSONに変換するwriteValue()メソッドを提供しています。ObjectMapperクラスは再利用が可能であり、シングルトンオブジェクトとして1度だけ初期化することができます。readValue()メソッドとwriteValue()メソッドには、バイト配列、ファイル、入出力ストリーム、およびリーダー/ライターオブジェクトを使用するための多くのオーバーロードされたバージョンがあります。
Jackson JSON – JSONをマップに変換する
時々、data.txtファイルに以下のようなJSONオブジェクトがあります。
{
"name": "David",
"role": "Manager",
"city": "Los Angeles"
}
そして、私たちはそれをマップに変換したい、そして同じプロパティとキーを持つJavaオブジェクトではなく。私たちは、以下のコードでJackson JSON APIを使ってとても簡単にそれを行うことができます。
//converting json to Map
byte[] mapData = Files.readAllBytes(Paths.get("data.txt"));
Map<String,String> myMap = new HashMap<String, String>();
ObjectMapper objectMapper = new ObjectMapper();
myMap = objectMapper.readValue(mapData, HashMap.class);
System.out.println("Map is: "+myMap);
//another way
myMap = objectMapper.readValue(mapData, new TypeReference<HashMap<String,String>>() {});
System.out.println("Map using TypeReference: "+myMap);
上記のスニペットを実行すると、以下の出力が得られます。
Map is: {name=David, role=Manager, city=Los Angeles}
Map using TypeReference: {name=David, role=Manager, city=Los Angeles}
Jackson JSON – 特定のJSONキーの読み込み
時には私たちにはJSONデータがあり、私たちはそのうちのほんのいくつかのキーの値に興味があります。その場合、JSON全体をオブジェクトに変換するのは良いアイデアではありません。Jackson JSON APIでは、DOMパーサのようなツリー形式でJSONデータを読み取るオプションが提供されており、これを通じてJSONオブジェクトの特定の要素を読み取ることができます。以下のコードは、JSONファイルから特定のエントリを読み取るためのスニペットを提供しています。
//read json file data to String
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//read JSON like DOM Parser
JsonNode rootNode = objectMapper.readTree(jsonData);
JsonNode idNode = rootNode.path("id");
System.out.println("id = "+idNode.asInt());
JsonNode phoneNosNode = rootNode.path("phoneNumbers");
Iterator<JsonNode> elements = phoneNosNode.elements();
while(elements.hasNext()){
JsonNode phone = elements.next();
System.out.println("Phone No = "+phone.asLong());
}
上記のコードスニペットを実行すると、以下の出力が得られます。
id = 123
Phone No = 123456
Phone No = 987654
Jackson JSON – JSON文書の編集
Jackson JSON Java APIは、JSONデータにキーを追加、編集、削除するための便利なメソッドを提供し、それを新しいJSONファイルとして保存したり、任意のストリームに書き込むことができます。以下のコードは、これを簡単に行う方法を示しています。
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
ObjectMapper objectMapper = new ObjectMapper();
//create JsonNode
JsonNode rootNode = objectMapper.readTree(jsonData);
//update JSON data
((ObjectNode) rootNode).put("id", 500);
//add new key value
((ObjectNode) rootNode).put("test", "test value");
//remove existing key
((ObjectNode) rootNode).remove("role");
((ObjectNode) rootNode).remove("properties");
objectMapper.writeValue(new File("updated_emp.txt"), rootNode);
もし上記のコードを実行し、新しいファイルを探すと、”role”と”properties”のキーが存在しないことに気づくでしょう。さらに、”id”の値が500に更新され、”test”という新しいキーがupdated_emp.txtファイルに追加されていることも気づくでしょう。
Jackson JSON Streaming APIの例
ジャクソンのJSON Java APIには、大きなJSONデータを扱う際に役立つストリーミングサポートも提供されています。このサポートにより、APIはファイル全体をトークンとして読み込み、より少ないメモリを使用します。ただし、ストリーミングAPIの唯一の問題は、JSONデータを解析する際にすべてのトークンに注意を払う必要があることです。例えば、{ “role”: “Manager” }というJSONデータがあると、順番に次のトークンが得られます – {(開始オブジェクト)、”role”(キー名)、”Manager”(キーの値)および}(終了オブジェクト)。コロン(:)はJSONの区切り記号であり、トークンとは見なされません。
package com.scdev.jackson.json;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.scdev.jackson.model.Address;
import com.scdev.jackson.model.Employee;
public class JacksonStreamingReadExample {
public static void main(String[] args) throws JsonParseException, IOException {
//create JsonParser object
JsonParser jsonParser = new JsonFactory().createParser(new File("employee.txt"));
//loop through the tokens
Employee emp = new Employee();
Address address = new Address();
emp.setAddress(address);
emp.setCities(new ArrayList<String>());
emp.setProperties(new HashMap<String, String>());
List<Long> phoneNums = new ArrayList<Long>();
boolean insidePropertiesObj=false;
parseJSON(jsonParser, emp, phoneNums, insidePropertiesObj);
long[] nums = new long[phoneNums.size()];
int index = 0;
for(Long l :phoneNums){
nums[index++] = l;
}
emp.setPhoneNumbers(nums);
jsonParser.close();
//print employee object
System.out.println("Employee Object\n\n"+emp);
}
private static void parseJSON(JsonParser jsonParser, Employee emp,
List<Long> phoneNums, boolean insidePropertiesObj) throws JsonParseException, IOException {
//loop through the JsonTokens
while(jsonParser.nextToken() != JsonToken.END_OBJECT){
String name = jsonParser.getCurrentName();
if("id".equals(name)){
jsonParser.nextToken();
emp.setId(jsonParser.getIntValue());
}else if("name".equals(name)){
jsonParser.nextToken();
emp.setName(jsonParser.getText());
}else if("permanent".equals(name)){
jsonParser.nextToken();
emp.setPermanent(jsonParser.getBooleanValue());
}else if("address".equals(name)){
jsonParser.nextToken();
//nested object, recursive call
parseJSON(jsonParser, emp, phoneNums, insidePropertiesObj);
}else if("street".equals(name)){
jsonParser.nextToken();
emp.getAddress().setStreet(jsonParser.getText());
}else if("city".equals(name)){
jsonParser.nextToken();
emp.getAddress().setCity(jsonParser.getText());
}else if("zipcode".equals(name)){
jsonParser.nextToken();
emp.getAddress().setZipcode(jsonParser.getIntValue());
}else if("phoneNumbers".equals(name)){
jsonParser.nextToken();
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
phoneNums.add(jsonParser.getLongValue());
}
}else if("role".equals(name)){
jsonParser.nextToken();
emp.setRole(jsonParser.getText());
}else if("cities".equals(name)){
jsonParser.nextToken();
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
emp.getCities().add(jsonParser.getText());
}
}else if("properties".equals(name)){
jsonParser.nextToken();
while(jsonParser.nextToken() != JsonToken.END_OBJECT){
String key = jsonParser.getCurrentName();
jsonParser.nextToken();
String value = jsonParser.getText();
emp.getProperties().put(key, value);
}
}
}
}
}
JsonParserは、JacksonのJSONストリーミングAPIであり、ファイルからデータを読み取るために使用しています。そしてparseJSON()メソッドは、トークンをループして処理し、Javaオブジェクトを作成するために使用されています。「address」はネストされたオブジェクトであるため、parseJSON()メソッドが再帰的に呼び出されることに注意してください。配列を解析するために、JSONドキュメントをループしています。ストリーミングAPIを使用してJSONデータを生成するためには、JsonGeneratorクラスを使用できます。
package com.scdev.jackson.json;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Set;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.scdev.jackson.model.Employee;
public class JacksonStreamingWriteExample {
public static void main(String[] args) throws IOException {
Employee emp = JacksonObjectMapperExample.createEmployee();
JsonGenerator jsonGenerator = new JsonFactory()
.createGenerator(new FileOutputStream("stream_emp.txt"));
//for pretty printing
jsonGenerator.setPrettyPrinter(new DefaultPrettyPrinter());
jsonGenerator.writeStartObject(); // start root object
jsonGenerator.writeNumberField("id", emp.getId());
jsonGenerator.writeStringField("name", emp.getName());
jsonGenerator.writeBooleanField("permanent", emp.isPermanent());
jsonGenerator.writeObjectFieldStart("address"); //start address object
jsonGenerator.writeStringField("street", emp.getAddress().getStreet());
jsonGenerator.writeStringField("city", emp.getAddress().getCity());
jsonGenerator.writeNumberField("zipcode", emp.getAddress().getZipcode());
jsonGenerator.writeEndObject(); //end address object
jsonGenerator.writeArrayFieldStart("phoneNumbers");
for(long num : emp.getPhoneNumbers())
jsonGenerator.writeNumber(num);
jsonGenerator.writeEndArray();
jsonGenerator.writeStringField("role", emp.getRole());
jsonGenerator.writeArrayFieldStart("cities"); //start cities array
for(String city : emp.getCities())
jsonGenerator.writeString(city);
jsonGenerator.writeEndArray(); //closing cities array
jsonGenerator.writeObjectFieldStart("properties");
Set<String> keySet = emp.getProperties().keySet();
for(String key : keySet){
String value = emp.getProperties().get(key);
jsonGenerator.writeStringField(key, value);
}
jsonGenerator.writeEndObject(); //closing properties
jsonGenerator.writeEndObject(); //closing root object
jsonGenerator.flush();
jsonGenerator.close();
}
}
JsonGeneratorはJsonParserに比べて使いやすいです。これはJackson JSON Parser Java APIのクイックリファレンスチュートリアルについての説明です。Jackson JSON Java APIは使いやすく、JSONデータを扱う開発者の利便性のために多くのオプションを提供しています。以下のリンクからプロジェクトをダウンロードし、操作してJackson Json APIについてさらに多くのオプションを探索してみてください。
Jackson JSONプロジェクトをダウンロードする
参照: ジャクソンのGitHubページ