Java で JSON を扱うには、 Jackson というライブラリが便利らしい。
Jackson は、POJO (Plain Old Java Object) であるシンプルなクラスを書いて、そこに JSON をマッピングするという機能が有名とか。
今回は、わざわざ新しく POJO なクラスを書くのではなく、 Java 標準ライブラリの java.util.Map と java.util.List にマッピングする方法を試してみた。
Jackson で JSON を Map や List に変換できると何が便利なのか
JSON を Java オブジェクトに変換するコードを用意しておくことで、ビジネスロジックからは Jackson の存在を意識しないでコードを書くことができる。
JSON から Java オブジェクトへのシンプルなマッピングのパターン
JSON データ型 | Java オブジェクト型 |
---|---|
オブジェクト (キーと値) | Map |
配列 | List |
文字列 | String |
整数 | Integer |
浮動小数点数 | Double |
真偽値 | Boolean |
null | null |
公式ドキュメント JacksonDataBinding - FasterXML Wiki には、変換できる Java オブジェクトについて書かれている。オブジェクトは LinkedHashMap、配列は ArrayList、整数は Integer / Long / BigInteger 、浮動小数点数は Double / BigDecimal へ変換できるらしい。
トップレベルがオブジェクトである JSON を Map に変換するサンプルコード
// JSON オブジェクトは Map<String, Object> にマッピングされる
private static Map<String, Object> readJsonObject(File json)
throws JsonMappingException, JsonParseException, IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
}
トップレベルが配列である JSON を List に変換するサンプルコード
// JSON 配列は List<Object> にマッピングされる
private static List<Object> readJsonArray(File json)
throws JsonMappingException, JsonParseException, IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, new TypeReference<List<Object>>(){});
}
Jackson のダウンロード
Jackson は複数の JAR ファイルで構成されていて、今回必要なのは、 Jackson Core, Jackson Annotations, Jackson databind の3つ。
Jackson の JAR ファイルは Central Maven repository からダウンロードできる。
For non-Maven use cases, you download jars from Central Maven repository or Wiki.
FasterXML/jackson-core · GitHub
今回ダウンロードするのは、安定版の最新バージョン 2.5.3 で、以下の3ファイル。
(って今みたら 2.5.4 がリリースされていた。。。)
JSON ファイルを読み込んで Java オブジェクトへ変換するサンプルコード
3種類のJSONファイルを読み込むサンプルコード。
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonSample {
public static void main(String[] args) throws Exception {
readJsonObjectSample();
readJsonArraySample();
readJsonArrayStringSample();
}
// JSON オブジェクトは Map<String, Object> にマッピングされる
private static Map<String, Object> readJsonObject(File json)
throws JsonMappingException, JsonParseException, IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
}
// JSON 配列は List<Object> にマッピングされる
private static List<Object> readJsonArray(File json)
throws JsonMappingException, JsonParseException, IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, new TypeReference<List<Object>>(){});
}
private static void readJsonObjectSample()
throws JsonMappingException, JsonParseException, IOException {
System.out.println("***** readJsonObjectSample *****");
File json = new File("object.json");
// JSON オブジェクトは Map<String, Object> にマッピングされる
Map<String, Object> map = readJsonObject(json);
System.out.println("name: " + map.get("name"));
System.out.println("age: " + map.get("age"));
System.out.println("score: " + map.get("score"));
System.out.println("alive: " + map.get("alive"));
// null は null
if(map.get("nulltest") == null){
System.out.println("nulltest is null.");
}else{
System.out.println("nulltest is " + map.get("nulltest"));
}
// JSON 配列は List にマッピングされる
List<String> friends = (List<String>) map.get("friends");
for (String friend : friends) {
System.out.println("friend: " + friend);
}
// 整数は Integer 型にマッピングされる
List<Integer> integers = (List<Integer>) map.get("integers");
for (Integer i : integers) {
System.out.println("integer: " + i);
}
// 浮動小数点数は Double 型にマッピングされる
List<Double> floats = (List<Double>) map.get("floats");
for (Double f : floats) {
System.out.println("float: " + f);
}
// 真偽値は Boolean 型にマッピングされる
List<Boolean> booleans = (List<Boolean>) map.get("booleans");
for (Boolean b : booleans) {
System.out.println("boolean: " + b);
}
// オブジェクトは Map 型にマッピングされる
Map<String, Object> favorites = (Map<String, Object>) map.get("favorites");
for (String key : favorites.keySet()) {
System.out.println("favorite: " + key + " = " + favorites.get(key));
}
}
private static void readJsonArraySample()
throws JsonMappingException, JsonParseException, IOException {
System.out.println("***** readJsonArraySample *****");
File json = new File("array.json");
// JSON 配列は List
List<Object> list = readJsonArray(json);
for (Object obj : list) {
// JSON オブジェクトは Map<String, Object> にマッピング
Map<String, Object> x = (Map<String, Object>) obj;
System.out.println("name: " + x.get("name"));
System.out.println("age: " + x.get("age"));
}
}
private static void readJsonArrayStringSample()
throws JsonMappingException, JsonParseException, IOException {
System.out.println("***** readJsonArrayStringSample *****");
File json = new File("array_string.json");
// JSON 配列は List<Object> にマッピング
List<Object> list = readJsonArray(json);
for (Object obj : list) {
// 文字列の JSON 配列
String name = (String)obj;
System.out.println("name: " + name);
}
}
}
読み込むJSONファイル (object.json)
トップレベルがオブジェクト。
{
"name" : "Alice",
"age" : 10,
"score" : 12.34,
"alive" : true,
"nulltest" : null,
"friends" : [
"Bob", "Carol"
],
"integers" : [
1, -2147483648, 2147483647
],
"floats" : [
1.5, -0.1
],
"booleans" : [
true, false
],
"favorites" : {
"book" : "Alice's Adventures in Wonderland",
"food" : "candy"
}
}
読み込むJSONファイル (array.json)
トップレベルが配列。
[
{
"name" : "Alice",
"age": 10
},
{
"name" : "Bob",
"age": 20
},
{
"name" : "Carol",
"age": 30
}
]
読み込むJSONファイル (array_string.json)
トップレベルが文字列の配列。
[ "Alice", "Bob", "Carol" ]
サンプルコードをコンパイル
今回の環境は、Mac OS X Yosemite + Java 1.8
警告が出るけど気にしない。
$ javac -classpath jackson-core-2.5.3.jar:jackson-annotations-2.5.3.jar:jackson-databind-2.5.3.jar JacksonSample.java
注意:JacksonSample.javaの操作は、未チェックまたは安全ではありません。
注意:詳細は、-Xlint:uncheckedオプションを指定して再コンパイルしてください。
実行結果
$ java -classpath ./:jackson-core-2.5.3.jar:jackson-annotations-2.5.3.jar:jackson-databind-2.5.3.jar JacksonSample
***** readJsonObjectSample *****
name: Alice
age: 10
score: 12.34
alive: true
nulltest is null.
friend: Bob
friend: Carol
integer: 1
integer: -2147483648
integer: 2147483647
float: 1.5
float: -0.1
boolean: true
boolean: false
favorite: book = Alice's Adventures in Wonderland
favorite: food = candy
***** readJsonArraySample *****
name: Alice
age: 10
name: Bob
age: 20
name: Carol
age: 30
***** readJsonArrayStringSample *****
name: Alice
name: Bob
name: Carol
Jackson で JSON を Map や List に変換できると便利だが欠点もある
便利だが、 POJO にマッピングしないということは、ひとつひとつデータを取り出すコードを自前で書く必要が出てくる。
JSON を Java で扱うのはなかなかめんどい。せめて XML の XPath のようなものが JSON にあればいいのにと思う。
参考資料
- JacksonHome - FasterXML Wiki
- JacksonDataBinding - FasterXML Wiki
- Convert JSON to Hashmap using Jackson | Level Up Lunch
tags: java json jackson
Posted by NI-Lab. (@nilab)