minimal-json で JSON を扱う

# 概要
Java の JSON ライブラリ [minimal-json](https://github.com/ralfstx/minimal-json) の使い方について説明します。

## Java で JSON を扱う
これまで(~JDK 1.8)の Java には標準で JSON ライブラリが提供されていないので、JSON を効率的に扱うには何かしらのライブラリを用いる必要がありました。OSS の有名どころだと下記が挙げられます。

| Library | License | Link
|:---|:---|:---
| org.json | JSON | [MVNRepository.com](https://mvnrepository.com/artifact/org.json/json)
| Jackson  | Apache License 2.0 | [GitHub](https://github.com/FasterXML/jackson-core) / [MVNRepository.com](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core)
| JSONIC   | Apache License 2.0 | [Official site](http://jsonic.osdn.jp/index.html) / [MVNRepository.com](https://mvnrepository.com/artifact/net.arnx/jsonic)
| Gson     | Apache License 2.0 | [GitHub](https://github.com/google/gson) / [MVNRepository.com](https://mvnrepository.com/artifact/com.google.code.gson/gson)
| JsonPath | Apache License 2.0 | [GitHub](https://github.com/jayway/JsonPath) / [MVNRepository.com](https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path)

それぞれのライブラリの使い方は Qiita やインターネットで検索すれば見つかると思いますので、この記事では取り上げません。

## [minimal-json](https://github.com/ralfstx/minimal-json)？
名前の通り、 JSON の読み書きにフォーカスした小さな Java ライブラリです。[MIT ライセンス](https://github.com/ralfstx/minimal-json/blob/master/LICENSE) で公開されています。リポジトリの README.md を見ると、「これ本当なの？」と言いたくなるような[高パフォーマンスを示すグラフ](https://camo.githubusercontent.com/0491bfbe2be5decebfebfe0733cba3062e2b5c50/68747470733a2f2f7261772e6769746875622e636f6d2f72616c667374782f6d696e696d616c2d6a736f6e2f6d61737465722f636f6d2e65636c69707365736f757263652e6a736f6e2e706572666f726d616e6365746573742f706572666f726d616e63652d7261702e706e67)が出てきます。

クラス数はさほど多くなく、バージョン 0.9.4 では下記の15個だけのようです。jar のファイルサイズは 29.7 KB です。

https://github.com/ralfstx/minimal-json/tree/master/com.eclipsesource.json/src/main/java/com/eclipsesource/json

1. Json.java
2. JsonArray.java
3. JsonHandler.java
4. JsonLiteral.java
5. JsonNumber.java
1. JsonObject.java
1. JsonParser.java
1. JsonString.java
1. JsonValue.java
1. JsonWriter.java
1. Location.java
1. ParseException.java
1. PrettyPrint.java
1. WriterConfig.java
1. WritingBuffer.java

### 開発状況
[GitHub の Release](https://github.com/ralfstx/minimal-json/releases/tag/0.9.4) によると、最後のリリースが 2015/07/20 で、[最新 commit は半年前の 2016/10/25](https://github.com/ralfstx/minimal-json/commits/master) でした。開発自体はまだ続いているようです。

## なぜ minimal-json か？
単純な JSON の読み書きをしたいだけであれば Gson や Jackson だと機能が多すぎ、シンプルな機能だけを持っている org.json は JSON ライセンス[^json]ということで、意外に選択肢がありませんでした。この minimal-json は JSON のパースにしか使えませんが、前述の通り15のクラスだけで構成されており、 [MIT ライセンス](https://github.com/ralfstx/minimal-json/blob/master/LICENSE)であるため、org.json を使いたい場合の代替として選択肢になりうる存在です。

なお、このライブラリには Gson や Jackson で提供されているような、オブジェクトとのマッピングをする機能はありませんので、そういった機能が必要であるなら、マッピングのコードを自分で実装するよりは別のライブラリを使う方がよいでしょう。

----
# 導入
build.gradle の dependencies に1行追加するだけです。記事を書いている段階での最新バージョンは 0.9.4 でした。

## Gradle

```groovy:build.gradle
dependencies {
    compile 'com.eclipsesource.minimal-json:minimal-json:0.9.4'
```

## Maven

```xml:pom.xml
<dependency>
    <groupId>com.eclipsesource.minimal-json</groupId>
    <artifactId>minimal-json</artifactId>
    <version>0.9.4</version>
</dependency>
```

上記以外は[こちら](https://mvnrepository.com/artifact/com.eclipsesource.minimal-json/minimal-json/0.9.4) をご覧ください。

----
# 使用

## JSON の文字列を生成
new で空のオブジェクトを生成し、それに add メソッドで key と値を追加できます。toString() メソッドで JSON 文字列にできます。

```java
final JsonObject json = new JsonObject();
json.add("name", "Bill");
json.add("age", 24);
System.out.println(json.toString());
```

```:出力
{"name":"Bill","age":24}
```

## 文字列から JsonObject オブジェクトの生成
Json#parse に JSON 文字列を渡し、asObject() で JSON Object を取り出します。

```java
final JsonObject json = Json.parse(jsonStr).asObject();
```

## 値の取り出し
String/int/double/float/long/boolean については専用のget～～メソッドがあります。読み込む JSON が完全にアプリケーション開発者の管理下に置かれていて、フォーマットが固定であるなら、これらのメソッドを使ってもよいでしょう。

key を文字列で指定して値を取り出します。第2引数には key がなかった時のデフォルト値を指定します。

```java
json.getString("name", "")
```

例えば、下記のようなシンプルな JSON があるとします。

```json
{"name": "John", "age": 28}
```

これをパースして値を取り出し、標準出力に表示するコードは下記の通りです。

```:java
final JsonObject json = Json.parse("{\"name\": \"John\", \"age\": 28}").asObject();
System.out.println(json.getString("name", "-"));
System.out.println(json.getInt("age", -1));
```

なお、 ```age``` は int なので、String として取り出そうとすると ```java.lang.UnsupportedOperationException``` が出ます。

```java:例外の出るコード
System.out.println(json.getString("age", "-"));
```

```:例外とメッセージ
java.lang.UnsupportedOperationException: Not a string: 28
```

int の値を String として取り出したい場合は get(key) で取り出した JsonValue オブジェクトを toString() するとよさそうです。

```java
System.out.println(json.get("age").toString());
```

ただ、その場合は json.get("age") が null を返し得ることに注意してください。

```java
final JsonValue nullable = json.get("age");
if (nullable != null) {
    System.out.println(nullable.toString());
}
```

Optional が使える環境であれば併用するとよいでしょう。

```java
Optional.ofNullable(json.get("age"))
        .map(JsonValue::toString)
        .ifPresent(System.out::println);
```


### null 対応
例えば、下記のようなシンプルな JSON があるとします。

```json
{"name": "John", "age": 28, "nullable": null}
```

"nullable" を getString しようとすると、下記のエラーが出ます。 defaultValue を役立ててほしいところですが……

```
java.lang.UnsupportedOperationException: Not a string: null
	at com.eclipsesource.json.JsonValue.asString(JsonValue.java:389)
	at com.eclipsesource.json.JsonObject.getString(JsonObject.java:674)
	at jp.toastkid.libs.epub.EpubMetaData.readJson(EpubMetaData.java:114)
```

ちなみに、下記だと例外は回避できますが、標準出力に null と表示されます。

```java
Optional.ofNullable(json.get("nullable"))
        .map(JsonValue::toString)
        .ifPresent(System.out::println);
```

「Optional ってそういう仕様だったかな」と思って、下記のコードを実行したら解決しました。この null は Java の null ではなく JSON リテラルの null ([com.eclipsesource.json.JsonLiteral](https://github.com/ralfstx/minimal-json/blob/0.9.4/com.eclipsesource.json/src/main/java/com/eclipsesource/json/JsonLiteral.java))として解釈されていたようです。

```java:調査用コード
Optional.ofNullable(json.get("nullable"))
        .map(Object::getClass)
        .map(Class::getName)
        .ifPresent(System.out::println);
```

```:出力
com.eclipsesource.json.JsonLiteral
```

えー、ここまで見てきてそろそろお気づきかと思います。このライブラリで JSON から取り出した値を扱う場合は get で取り出して JsonValue オブジェクトを介してアクセスした方が安全ですし、 null チェックも容易です。Optional を組み合わせるとよりいい具合になります。

----
# 残念な点

## JsonArray が Java 8 対応不十分
[JsonArray クラスは Iterable の実装](https://github.com/ralfstx/minimal-json/blob/master/com.eclipsesource.json/src/main/java/com/eclipsesource/json/JsonArray.java#L63) です。stream() がないので Stream API が使えず、イテレーションに使えるメソッドは forEach くらいしか提供されていません。せめて filter / map くらいは欲しいところです。

### has 系のメソッドがない
なぜ Optional を持ち出したのかというと、キーの存在を確認するメソッドがないからです。うーん、そう考えると、このライブラリを 1.7 以下で使うのはちょっときついのではないかと……if での null チェックが大量に並ぶコードになります。

----
# まとめ
ごく簡単な JSON の操作ができる Java ライブラリ minimal-json の特徴と導入方法について述べました。値の取り出しには get(key) を使った方が安全で、 Optional と組み合わせて使うと良い感じになります。

[^json]: ```The Software shall be used for Good, not Evil.``` の解釈で、保守的なソフトウェアライセンス管理ポリシーを採用している会社だと、JSON ライセンスのライブラリは使用を認められないケースがあります。「JSON ライセンスは org.json のコードには適用されていない」という見解もあるようですが、Java の JSON ライブラリで代替品がある以上は無用のリスクを抱える必要もない、という判断がされる可能性が高いのではないかと思います。ちなみに、Android SDK には [org.json のライブラリが含まれている](https://developer.android.com/reference/org/json/package-summary.html) のですが、特にライセンス面で問題があるという話は聞いていません。