jacksonで検索すると、アメリカの楽器メーカーの Jackson Guitars とか、歌手のMichael Jackson 氏とかヒットするけれど、今回は、
This is the home page of the Jackson Project, formerly known as the standard JSON library for Java (or JVM platform in general), or, as the "best JSON parser for Java." Or simply as "JSON for Java." More than that, Jackson is a suite of data-processing tools for Java (and the JVM platform), including the flagship streaming JSON parser / generator library, matching data-binding library (POJOs to and from JSON) and additional data format modules to process data encoded in Avro, BSON,CBOR, CSV, Smile, (Java) Properties, Protobuf, XML or YAML; and even the large set of data format modules to support data types of widely used data types such as Guava, Joda, PCollections and many, many more (see below).
⇧ JavaのJSONライブラリのJacksonの話でございます。
ジャクソン5 は「Jackson 5」で検索しないとヒットしなかったという...
というわけで、Jacksonにトライ。
Jacksonを導入してみる
Jacksonについては、
⇧ 上記サイト様が詳しいです。
というわけで、まずは、Jacksonライブラリをダウンロードして配置ですかね。(Maven、Gradle、Bazel などのビルドツールとか使える環境であれば、ライブラリを依存関係とかも良しなに取り計らってインストールくれるようですが)
では、https://github.com/FasterXML/jackson にアクセスし、
スクロールすると、「Recommended way to use Jackson is through Maven repositories; releases are made to Central Maven Repository (CMR). 」ってある、というか、おすすめっていう割には、「Central Maven Repository」へのリンク作ってないんだ...
というわけで、https://mvnrepository.com/repos/central にアクセス。
「jackson」で検索。
かなりヒットするんですが、「Jackson Databind」「Jackson Core」「Jackson Annotations」の3つがあれば良いらしい。
Java Jacksonを使用したJSONの変換のサンプル |
⇧ 上記サイト様によりますと、Mavenを使える場合は、「Jackson Core」をインストールするよう、pom.xmlに記述すると、他の2つのライブラリも勝手にインストールしてくれるんだとか。
今回は、Mavenを使ってないので、3つ全てダウンロードします。
まずは、「Jackson Core」をクリック。
ページ遷移するので、今回は、「Version」が「2.9.6」のリンクをクリック。
「Files」の「View All」のリンクをクリック。
「jackson-core-2.9.6.jar」を選択。
「保存」をクリック。
保存されました。
「Jackson Databind」「Jackson Annotations」についても同じ手順でjarファイルをダウンロードします。
ダウンロードされました。
Eclipseを起動し、 「ファイル(F)」>「新規(N)」>「Java プロジェクト」を選択。
「プロジェクト名(P):」を適当に付けて、「次へ(N) >」をクリック。
「完了(F)」をクリック。
「パッケージ・エクスプローラー」にプロジェクトが作成されるので、プロジェクトを選択した状態で右クリックし、「新規(W)」>「フォルダー」をクリック。
プロジェクトディレクトリの直下(ここでは、「Jackson Test」)に、ライブラリ配置用のディレクトリを作成。「フォルダー名(N):」は適当に。「完了(F)」 をクリック。
作成した、ライブラリ配置用のディレクトリに、ダウンロードしておいたライブラリをドラッグ&ドロップします。
「ファイルをコピー(C)」にチェックされた状態で、「OK」。
配置されました。
ライブラリを使えるようにビルドパスに追加します。
「パッケージ・エクスプローラー」でプロジェクトを選択した状態で右クリックし、「ビルド・パス(B)」>「外部アーカイブの追加(V)...」を選択。
配置しておいたライブラリを選択し、「開く(O)」。(Shiftを押しながらクリックで複数ファイルを選択できます。全選択は、フォルダ内で、Ctrl + A で。)
「参照ライブラリー」が追加され、その中にライブラリがあればOKです。
Jacksonを使ってみる
Jacksonが使える準備が整いましたので、クラスファイルを作成します。
プロジェクトを選択した状態で右クリックし、「新規(W)」>「クラス」を選択。
「パッケージ(K):」「名前(M):」を適当に入力し、「public static void main(String[] args)(V)」にチェックし、「完了(F)」をクリック。
クラスができました。
で、今回も、
{ "id": "957c43f2-fa2e-42f9-bf75-6e3d5bb6960a", "name": "The Best Product", "brand": { "id": "9bcd817d-0141-42e6-8f04-e5aaab0980b6", "name": "Baccarat", "owner": { "id": "b21a80b1-0c09-4be3-9ebd-ea3653511c13", "name": "Fortune Legend Limited", "manager" : { "id" : "98jughul-078b-ih09-kju9-p09juhugh086", "name" : null, "employee" : { "id" : "98jughul-078b-ih09-kju9-p09lhrs54676", "name" : null } } } } }
のようなJSONデータが、外部から渡された体で。
例のごとく、
で、なんやかんややってみて、どうも、JacksonでJSONデータのNULLを空文字に置換する処理が必要そう。
⇧ 上記サイト様によりますと、 なんか、Jacksonでパーサー(解析)をする前に、JSONデータのNULLを空文字に置換するしかなさそう。
⇧ 上記サイト様とかの方法だと、JsonNodeがNULLの場合に、エラーになるっぽい
((ObjectNode)jsonNode).put("value", "");
みたいなことを、JsonNodeがNULLの場合にやろうとすると、
Exception in thread "main" java.lang.ClassCastException: com.fasterxml.jackson.databind.node.NullNode cannot be cast to com.fasterxml.jackson.databind.node.ObjectNode at ts0818.jackson.test.JacksonTest.objectNullCheck(JacksonTest.java:95) at ts0818.jackson.test.JacksonTest.objectNullCheck(JacksonTest.java:92) at ts0818.jackson.test.JacksonTest.objectNullCheck(JacksonTest.java:92) at ts0818.jackson.test.JacksonTest.nodeTypeChecker(JacksonTest.java:56) at ts0818.jackson.test.JacksonTest.lambda$0(JacksonTest.java:35) at java.lang.Iterable.forEach(Iterable.java:75) at ts0818.jackson.test.JacksonTest.main(JacksonTest.java:34)
みたいなエラーが。
Jacksonが提供するObjectMapperに独自定義したシリアライザーをDefaultSerializerProviderを通して設定してやればよい。
⇧ 上記サイト様によりますと、そういうことみたいです。Spring Bootではないですが、やってみますか。
シリアライザー用のクラスを1つ追加します。
「パッケージ(K):」「名前(M):」を適当に入力し、「完了(F)」をクリック。
NullValueSerializer.java
package ts0818.jackson.test; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; public class NullValueSerializer extends JsonSerializer<Object> { @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { // TODO 自動生成されたメソッド・スタブ jgen.writeString(""); } }
JacksonTest.java
package ts0818.jackson.test; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.Map; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeType; import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; public class JacksonTest { public ObjectMapper mapper; public JacksonTest() { // JacksonのObjectMapper mapper = new ObjectMapper(); DefaultSerializerProvider.Impl dsp = new DefaultSerializerProvider.Impl(); dsp.setNullValueSerializer(new NullValueSerializer()); mapper.setSerializerProvider(dsp); } public static void main(String[] args) { // JSONデータ String json = "{\r\n \"id\": \"957c43f2-fa2e-42f9-bf75-6e3d5bb6960a\",\r\n \"name\": \"The Best Product\",\r\n \"brand\": {\r\n \"id\": \"9bcd817d-0141-42e6-8f04-e5aaab0980b6\",\r\n \"name\": \"Baccarat\",\r\n \"owner\": {\r\n \"id\": \"b21a80b1-0c09-4be3-9ebd-ea3653511c13\",\r\n \"name\": \"Fortune Legend Limited\",\r\n \"manager\" : {\r\n \"id\" : \"98jughul-078b-ih09-kju9-p09juhugh086\",\r\n \"name\" : null,\r\n \"employee\" : {\r\n \"id\" : \"98jughul-078b-ih09-kju9-p09lhrs54676\",\r\n \"name\" : null\r\n }\r\n }\r\n }\r\n } \r\n}"; InputStream input = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); JacksonTest jacksonTest = new JacksonTest(); JsonNode rootNode = null; try { // JSONデータをオブジェクトに変換 rootNode = jacksonTest.mapper.readValue(input, JsonNode.class); System.out.println(rootNode); rootNode.forEach(node -> { nodeTypeChecker(node); }); System.out.println(rootNode); } catch (IOException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } public static JsonNode nodeTypeChecker(JsonNode node) { switch(node.getNodeType()) { case ARRAY : System.out.println("ARRAY"); case OBJECT : System.out.println("OBJECT"); System.out.println(node); objectNullCheck(node); break; case POJO : System.out.println("POJO"); break; case BOOLEAN : System.out.println("BOOLEAN"); break; case BINARY : System.out.println("BINARY"); break; case MISSING : System.out.println("MISSING"); break; case NULL : System.out.println("NULL"); break; case NUMBER : System.out.println("NUMBER"); break; case STRING : System.out.println("STRING"); System.out.println(node); break; } return node; } public static JsonNode objectNullCheck(JsonNode node) { Iterator<Map.Entry<String, JsonNode>> nodeObjectFileds = node.fields(); Map.Entry<String, JsonNode> entry; while(nodeObjectFileds.hasNext()) { entry = nodeObjectFileds.next(); if(entry.getValue().getNodeType() == JsonNodeType.ARRAY || entry.getValue().getNodeType() == JsonNodeType.OBJECT) { objectNullCheck(entry.getValue()); } } return node; } }
そして、メインメソッドのあるクラス(ここでは、「jacksonTest.java」)を選択した状態で右クリックし、「実行(R)」>「Java アプリケーション」を選択。
はい、null は変わらずですけど?
全然できないんですけど...
もしかして、シリアライザーって、JavaオブジェクトからJSONを作るときにしか有効じゃないってこと?
ってなると、もう力技で、JSONデータからnullを検索して置換処理ってぐらいしか方法がなくなるんですが...。
ということで、
⇧ 上記サイト様を参考に、無理やりObjectをMapに置換しての荒業になりました...うまい方法が思いつかんですね(涙)。
というわけで、「JacksonTest02.java」を作成。(「JacksonTest.java」を編集すれば良かったんだけど、新しく作ったほうが早いと思い...)
package ts0818.jackson.test; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; public class JacksonTest02 { public static void main(String[] args) { // TODO 自動生成されたメソッド・スタブ // JSONデータ String json = "{\r\n \"id\": \"957c43f2-fa2e-42f9-bf75-6e3d5bb6960a\",\r\n \"name\": \"The Best Product\",\r\n \"brand\": {\r\n \"id\": \"9bcd817d-0141-42e6-8f04-e5aaab0980b6\",\r\n \"name\": \"Baccarat\",\r\n \"owner\": {\r\n \"id\": \"b21a80b1-0c09-4be3-9ebd-ea3653511c13\",\r\n \"name\": \"Fortune Legend Limited\",\r\n \"manager\" : {\r\n \"id\" : \"98jughul-078b-ih09-kju9-p09juhugh086\",\r\n \"name\" : null,\r\n \"employee\" : {\r\n \"id\" : \"98jughul-078b-ih09-kju9-p09lhrs54676\",\r\n \"name\" : null\r\n }\r\n }\r\n }\r\n } \r\n}"; ObjectMapper mapper = new ObjectMapper(); // LinkedHashMapで順番を保持 Map<String,Object> map = new LinkedHashMap<>(); try { // JSONから、LinkedHashMap<String, Object>を作って、Map<String, Object>に格納 map = mapper.readValue(json, new TypeReference<LinkedHashMap<String,Object>>(){}); System.out.println(map.toString()); // 再帰処理で、mapの中身を処理 mapChecker(map); } catch (Exception e) { e.printStackTrace(); return; } System.out.println(map.toString()); } public static Map<String, Object> mapChecker(Map<String, Object> targetMap) { // Mapの中身を、key=value の形で取得 for(Entry<String, Object> set : targetMap.entrySet()) { // valueでオブジェクトになってる部分は、LinkedHashMapになってるらしい if(set.getValue() != null && set.getValue().getClass() == LinkedHashMap.class) { System.out.println("Object : " + set.getValue().getClass()); // オブジェクトが、LinkedHashMapの場合は、同じ処理を(再帰) mapChecker(autoCast(set.getValue())); } if(set.getValue() == null) { //System.out.println(set.getValue()); targetMap.put(set.getKey().toString(), ""); } } return targetMap; } // キャストする用 @SuppressWarnings("unchecked") public static <T> T autoCast(Object obj) { T castObj = (T) obj; return castObj; } }
ということで、「実行(R)」>「Java アプリケーション」を選択。
nullが置換されました。
なんとか、NULLを空文字に置換はできたんですが、これ、JavaのClassのオブジェクトにマッピングできるんですかね?
マッピングできなかったら、なんの意味もないですしね...
そして、シリアライザー用のクラスとか結局まったく使わなかったという...
まぁ、今回はNULLチェックということなので、JSONからJavaオブジェクトへのマッピングは時間のあるときに勉強していきたいですね。
私も、先輩社員に教わりたい...そんな気持ちを常に抱いていますかね。
今回は、このへんで。
番外編
jacksonは、Version1 と version2 は互換性が無いというのを知らず、version1 のものを入れてハマりました。
というわけで、ここからさきは、間違って、JacksonのVersion1系を入れてしまったときの話ですかね。
https://mvnrepository.com/artifact/org.codehaus.jackson にアクセスしてライブラリをダウンロード。「Data Mapper For Jackson」をクリック。(Apacheライセンスのほうで大丈夫かと)
2013年で更新が止まってるのが気にはなるけど...「1.9.13」のリンクをクリック。
「View All」をクリック。
「jackson-mapper-asl-1.9.13.jar」をクリック。「保存」で。
ダウンロードされました。
プロジェクトのライブラリ用のディレクトリに配置します。
「ファイルをコピー(C)」にチェックされた状態で、「OK」。
そしたら、ビルド・パスの構成にライブラリを追加します。 「参照ライブラリ」からも追加できるようです。
「外部 JAR の追加(X)...」を選択。
ライブラリを選択し、「開く(O)」で。
「適用(A)」をクリック。
「OK」をクリック。
参照できるライブラリに追加されました。