JacksonのJsonNodeの使い方

# はじめに
たまに使おうとすると忘れるのでメモ
インストール方法や基本的な説明はしないのでそれが目的の人は他をあたって欲しい。

# 静的なクラス定義を使わない
Jackson を使う際、静的なクラス定義を使う場合と使わない場合で使い方が大きく異なる。
JSON表記に慣れると、Java のクラスなんていちいち書いていられない。
ここでは、**使わない方法だけ**を説明する。
アノテーション(<span>@</span>)も使わない。


# サンプルプログラム

要点は、**ObjectMapper** と **JsonNode** を使う。
例示したほうが、理解が早い

```java
import java.util.Iterator;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class Test {

	public static void main(String[] args) {
		
		try {
		
			String json = 
				"{\"organization\":\"会員\",\"persons\":[" +
				  "{\"id\":123,\"name\":{\"first\":\"太郎\",\"last\":\"山田\"}}," +
				  "{\"id\":456,\"name\":{\"first\":\"花子\",\"last\":\"佐藤\"}}" +
				"]}";

			// Original
			System.out.println(json);
			
			// ObjectMapperを作成
			ObjectMapper mapper = new ObjectMapper();

			// (1) String -> Object(JsonNode)
			JsonNode jsonObj = mapper.readTree(json);

			// (2) String <- Object(JsonNode)
			String jsonStr = mapper.writeValueAsString(jsonObj);
			// 検証 original と同じになる
			System.out.println(jsonStr);
			
			// (3) JsonNode のツリー解析と表示 Original と同じになる
			trnJsonNode(jsonObj);
			System.out.println("");
		
			// (4) 定義の分かっているオブジェクトからの値の取得
			JsonNode jsonNode = jsonObj.get("organization");
			System.out.println("organization:" + jsonNode.textValue());
			// JsonNode aryNode =  jsonObj.get("persons"); // でも可
			ArrayNode aryNode =  (ArrayNode)jsonObj.get("persons");
			jsonNode = aryNode.get(0); // 1番目の配列要素
			int id = jsonNode.get("id").intValue();
			jsonNode = jsonNode.get("name");
			System.out.println("person：" 
			  + id + " " + jsonNode.get("last").textValue() + jsonNode.get("first").textValue());
			
			// (5) 要素の追加
			// ObjectNode objNode = mapper.createObjectNode();
			ObjectNode objNode = (ObjectNode)jsonObj;
			ObjectNode personNode = (ObjectNode)mapper.readTree(
					"{\"id\":789,\"name\":{\"first\":\"一郎\",\"last\":\"斎藤\"}}");
			ArrayNode personsArray = (ArrayNode)objNode.get("persons");
			personsArray.add(personNode);
			System.out.println(objNode.toString());
			
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
	}
	
	// JsonNode の動的解析
	static void trnJsonNode( JsonNode node ) {
		int n = 0; // ","の制御用
		
		System.out.print("{");
		Iterator<String> fieldNames = node.fieldNames();
		while (fieldNames.hasNext()) {
			if( n > 0 ) System.out.print(",");
			String fieldName = fieldNames.next();
			JsonNode curNode = node.get(fieldName);
			if( curNode.isArray()) {
				int n2=0; // ","の制御用
				System.out.print("\"" + fieldName + "\":");
				System.out.print("[");
		         for(JsonNode itemNode : curNode) {
		 			if( n2 > 0 ) System.out.print(",");
		             trnJsonNode(itemNode);
		             n2++;
		          }
				System.out.print("]");
				continue;
			}
			if( curNode.isObject()) {
				System.out.print("\"" + fieldName + "\":");
				trnJsonNode(curNode);
			} else {
				if( curNode.isNumber()) {
					System.out.print("\"" + fieldName + "\":" + curNode.decimalValue());
				} else {
                    // Todo isBoolean()、isNull()
					System.out.print("\"" + fieldName + "\":\"" + curNode.textValue() + "\"");
				}
			}
			n++;
		}
		System.out.print("}");		
	}
}
```
# JSON文字列と Javaオブジェクトとの相互変換
(1)(2)は JSON文字列と Javaオブジェクトとの相互変換の例。
JavaScript と違って、JSON 文字列を直接オブジェクトに変換できないので、
**ObjectMapper** を使って行う。
**JsonNode** は jackson で用意された汎用のクラス。
Map と Array の両方の性格があると思えば良い。

# JsonNode を理解する
ひとつひとつ説明しても全体が理解し難い。
(3)の例は未知の JsonNode の解析を再帰関数を使って行うもの。
JSON には配列があるが、JsonNode は **ArryNode** としても扱える。
明示的に配列として使う場合は、ArrayNode にキャストする。
なので、参照に限っては、あえて **JsonNode と ArryNode を使い分ける必要は無い**。
要点は、JsonNode のタイプを判定して処理を分ける。
値も正確にはいろいろなタイプがあるので必要ならもっと細かく分けられる。
普通は、数値か文字列かを区別すれば十分だろう。
蛇足だが、

```java
for(JsonNode itemNode : curNode) {
```

は、**ArrayNode** として処理されている。

```java
for(JsonNode itemNode : (ArrayNode)curNode) {
```

と書いても同じ。

# JsonNode から目的の値を取得する
一番使うのは、(4) の様に、JSON の形式が解っている場合ではないだろうか。
WEB API では JSON 文字列の使用例くらいしかドキュメントされないのが普通である。
これをわざわざ Java のクラス定義に置き換えてコーディングするのも馬鹿らしい。
サンプルでは、

```java
JsonNode node = jsonObj.get("organization");
System.out.println("organization:" + node.textValue());
```
形式が解っているので、あえて JsonNode のタイプを判定する必要は無い。
このように、JsonNode から値を取得するには、文字列なら textValue() 数値なら decimalValue() 等を使う。

:::note info
asText() や asInt() 等でも同じように動作する。
主な違いは、値が文字列以外の場合 textValue() は null を返すが、asText は空文字を返す。
:::

配列は、解りやすく

```java
ArrayNode aryNode =  (ArrayNode) jsonObj.get("persons");
```

としたが、JsonNode で受けても良い。

# 読み込んだオブジェクトを更新する
最後に(5)は読み込んだオブジェクトを更新したい場合。
更新は、**JsonNode** では無く、**ObjectNode** および **ArrayNode** を使う必要がある。
変換が面倒のように思われるが、

```java
ObjectNode objNode = (ObjectNode)jsonNode;
ArrayNode aryNode = (ArrayNode)jsonNode;
```

と参照方法を変えるだけで良い。
JsonNode はこのように連想配列と配列の両方の性格を持っている。

```java
ObjectNode objNode = mapper.createObjectNode();
```

で **空** から作成しても良いが、あえてその必要は無い。
ここの例では、追加する配列要素だけ **ObjectMapper** で作成し、既存のオブジェクトに追加している。

サンプルの実行結果は、

```json
{"organization":"会員","persons":[{"id":123,"name":{"first":"太郎","last":"山田"}},{"id":456,"name":{"first":"花子","last":"佐藤"}}]}
{"organization":"会員","persons":[{"id":123,"name":{"first":"太郎","last":"山田"}},{"id":456,"name":{"first":"花子","last":"佐藤"}}]}
{"organization":"会員","persons":[{"id":123,"name":{"first":"太郎","last":"山田"}},{"id":456,"name":{"first":"花子","last":"佐藤"}}]}
organization:会員
person：123 山田太郎
{"organization":"会員","persons":[{"id":123,"name":{"first":"太郎","last":"山田"}},{"id":456,"name":{"first":"花子","last":"佐藤"}},{"id":789,"name":{"first":"一郎","last":"斎藤"}}]}
```

# 最後に
JavaScript を使っていると、Java の冗長性には閉口する。
Jackson で JsonNode を使えば、ある程度同じような感覚で扱える。