Jackson JSON JavaパーサーAPIの例チュートリアル

Jackson JSON Java Parserは非常に人気があり、Springフレームワークでも使用されています。Java JSON Processing APIはあまり使いやすくなく、JsonからJavaオブジェクトへの自動変換機能も提供していません。幸いなことに、JSON処理に使用できる代替APIがいくつかあります。前回の記事では、Google Gson APIについて学び、その使い方がどれほど簡単かを見ました。

Jackson JSON Java Parser

プロジェクトで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ライブラリに依存しています。したがって、それらをビルドパスに直接追加する場合は、すべてを追加することを確認してください。そうしないと、ランタイムエラーが発生します。Jackson JSONパーサーAPIは、JSONをPOJOオブジェクトに簡単に変換する方法を提供し、JSONデータからのMapへの簡単な変換もサポートしています。Jacksonはジェネリクスもサポートしており、JSONからオブジェクトに直接変換します。

Jackson JSONの例

JSONからPOJO/Javaオブジェクトへの変換の例では、入れ子オブジェクトと配列を持つ複雑な例を取り上げます。変換のためにJavaオブジェクト内で配列、リスト、およびMapを使用します。複雑なJSONは、以下の構造でファイルemployee.txtに格納されています:

{
  "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"
  }
}

次のJavaクラスがJSONデータに対応しています。

package com.journaldev.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.journaldev.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;
	}
}

Employeeは、ルートJSONオブジェクトを表すJavaビーンです。では、Jackson JSONパーサーAPIを使用してJSONをJavaオブジェクトに変換する方法を見てみましょう。

package com.journaldev.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.journaldev.jackson.model.Address;
import com.journaldev.jackson.model.Employee;


public class JacksonObjectMapperExample {

	public static void main(String[] args) throws IOException {
		
		// JSONファイルデータを文字列に読み込む
		byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
		
		// ObjectMapperインスタンスを作成する
		ObjectMapper objectMapper = new ObjectMapper();
		
		// JSON文字列をオブジェクトに変換する
		Employee emp = objectMapper.readValue(jsonData, Employee.class);
		
		System.out.println("Employee Object\n"+emp);
		
		// オブジェクトをJSON文字列に変換する
		Employee emp1 = createEmployee();
		// オブジェクトマッパーをきれいに印刷するように設定する
		objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
		
		// コンソールに書き込みます。ファイルなどの任意の出力ストリームに書き込むことができます。
		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 cities = new ArrayList();
		cities.add("Los Angeles");
		cities.add("New York");
		emp.setCities(cities);

		Map props = new HashMap();
		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
//上記のJSONファイルデータを出力

com.fasterxml.jackson.databind.ObjectMapperは、Jackson APIで最も重要なクラスであり、JSONをJavaオブジェクトに変換し、JavaオブジェクトをJSONに変換するためのreadValue()およびwriteValue()メソッドを提供します。ObjectMapperクラスは再利用でき、シングルトンオブジェクトとして一度だけ初期化できます。readValue()およびwriteValue()メソッドのオーバーロードバージョンがたくさんあり、バイト配列、ファイル、入出力ストリーム、およびReader/Writerオブジェクトと連携するためのものです。

Jackson JSON – JSONをMapに変換

時折、data.txtファイルに以下のようなJSONオブジェクトがあり、それをJavaオブジェクトに変換せずに同じプロパティとキーを持つMapに変換したいことがあります。

{
  "name": "David",
  "role": "Manager",
  "city": "Los Angeles"
}

これは、Jackson JSON APIを使用して以下のコードで簡単に実行できます:

//jsonをMapに変換
byte[] mapData = Files.readAllBytes(Paths.get("data.txt"));
Map myMap = new HashMap();

ObjectMapper objectMapper = new ObjectMapper();
myMap = objectMapper.readValue(mapData, HashMap.class);
System.out.println("Map is: "+myMap);

//別の方法
myMap = objectMapper.readValue(mapData, new TypeReference>() {});
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ファイルから特定のエントリを読み取るスニペットを提供します。

// JSONファイルデータを文字列に読み込む
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));

// ObjectMapperインスタンスを作成する
ObjectMapper objectMapper = new ObjectMapper();

// DOMパーサーのようにJSONを読み取る
JsonNode rootNode = objectMapper.readTree(jsonData);
JsonNode idNode = rootNode.path("id");
System.out.println("id = "+idNode.asInt());

JsonNode phoneNosNode = rootNode.path("phoneNumbers");
Iterator 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();

// JsonNodeを作成する
JsonNode rootNode = objectMapper.readTree(jsonData);

// JSONデータを更新する
((ObjectNode) rootNode).put("id", 500);
// 新しいキー値を追加する
((ObjectNode) rootNode).put("test", "test value");
// 既存のキーを削除する
((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の例

Jackson JSON Java APIは、大きなJSONデータと作業する際に役立つストリーミングサポートも提供しています。これは、ファイル全体をトークンとして読み取り、メモリを少なく使用する特長があります。ストリーミングAPIの唯一の問題は、JSONデータの解析中にすべてのトークンに注意を払う必要があることです。もし、jsonデータが {“role”:“Manager”} であれば、以下の順序でトークンが得られます – { (開始オブジェクト)、“role” (キー名)、“Manager” (キーの値) そして } (終了オブジェクト)。コロン (:) はJSONの区切り文字であり、したがってトークンとしては考慮されません。

package com.journaldev.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.journaldev.jackson.model.Address;
import com.journaldev.jackson.model.Employee;

public class JacksonStreamingReadExample {

	public static void main(String[] args) throws JsonParseException, IOException {
		
		// JsonParserオブジェクトの作成
		JsonParser jsonParser = new JsonFactory().createParser(new File("employee.txt"));
		
		// トークンをループ
		Employee emp = new Employee();
		Address address = new Address();
		emp.setAddress(address);
		emp.setCities(new ArrayList());
		emp.setProperties(new HashMap());
		List phoneNums = new ArrayList();
		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();
		// 従業員オブジェクトを出力
		System.out.println("Employee Object\n\n"+emp);
	}

	private static void parseJSON(JsonParser jsonParser, Employee emp,
			List phoneNums, boolean insidePropertiesObj) throws JsonParseException, IOException {
		
		// 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();
				// ネストされたオブジェクト、再帰呼び出し
				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は、jsonデータを読み取るためのjackson json streaming APIであり、ファイルからデータを読み取り、その後parseJSON()メソッドを使用してトークンをループし、それらを処理してJavaオブジェクトを作成しています。parseJSON()メソッドが「address」の場合に再帰的に呼び出されることに注意してください。これは、jsonデータ内のネストされたオブジェクトです。配列の解析では、jsonドキュメントをループします。JsonGeneratorクラスを使用して、ストリーミングAPIでjsonデータを生成できます。

package com.journaldev.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.journaldev.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"));
		//整形して表示
		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 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プロジェクトのダウンロード

参考: Jackson GitHubページ

Source:
https://www.digitalocean.com/community/tutorials/jackson-json-java-parser-api-example-tutorial