JavaでJSONの値がNULLかどうか検証、JSONって入れ子になってることが多いけど、JSON in Java編

どうも、最近、OneRepublic の「Counting Star」という曲にハマっているボクです。

 

そうなんですよね~、JSONって入れ子構造になっていることが多くて、これ、どうやってNULLチェックしたら良いんだと。

そんなこんなで、今回は、入れ子構造の場合のJSONのNULLチェックにトライ。

Kubernetesのコンテナは絶賛調査中です...つまり解決に至っていません...。

 

まずは、JavaJSONを利用できるようにライブラリの導入を検討

Javaの標準ライブラリ(API)には用意されていないらしいので、外部からインストールするしかないらしい 。

と思ったのですが、

www.sejuku.net

⇧  上記サイト様によりますと、 

Java6からJavaScriptを扱うことができるようになり、RhinoというJavaScriptエンジンを使用していました。

Java8からは新たにNashornというJavaScriptエンジンが追加され、Rhinoから置き換えることができます。

【Java】JSONデータを標準API、Jackson、JSON in Javaで扱う方法 | 侍エンジニア塾ブログ | プログラミング入門者向け学習情報サイト 

JavaJavaScriptを扱うためには、まずScriptEngineManagerクラスを使ってインスタンスを生成します。

そのオブジェクトからgetEngineByNameメソッドを呼び出し、引数にエンジンnashornを指定します。

そして、evalメソッドを使って、JavaScriptを実行します。

【Java】JSONデータを標準API、Jackson、JSON in Javaで扱う方法 | 侍エンジニア塾ブログ | プログラミング入門者向け学習情報サイト

⇧  Java側で、JavaScriptを扱うことによって、JSONを実現する方法もあるようです。Node.jsなんかみたいに、サーバーサイドでJavaScriptを実装するイメージですかね?

 

まぁ、今回は、ライブラリを導入してみたいと思いますが、Javaで扱えるJSONライブラリとして、有名どころとしては、

ライブラリ名 パッケージ名
Jackson com.fasterxml.jackson
Gson com.google.code.gson
JSON in Java org.json

が、あるみたいです。 

その他にも、JSONを扱うライブラリとしては、 

http://json.org/

⇧  上記の公式サイトが詳しいです。

 

とりあえず、JSON in Java

とりあえず、生の人っていう居酒屋のノリと言うわけじゃないけど、一番機能の少なそうなJSON in Javaを試してみます。

Jacksonは、多機能すぎて、調べるのが大変そうということで、時間のありそうなときにチャレンジしたいということで。

http://json.org/ にアクセスして、

f:id:ts0818:20180916140416p:plain

下の方にスクロールすると、「JSON-java」ってあるので、クリック。

f:id:ts0818:20180916140555p:plain

Git Hubのページに遷移するので、一番したのほうにスクロールすると、

f:id:ts0818:20180916140733p:plain

https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav のリンクがあるので、クリック。

f:id:ts0818:20180916141325p:plain

リポジトリに遷移するので、  

f:id:ts0818:20180916140246p:plain

今回は、「Download」の「jar」をクリック。 

f:id:ts0818:20180916141757p:plain

ブラウザの下部に表示される選択肢で、「保存」を選択。 jarをダウンロードするときのこの説明は、毎回思うけど、怖すぎる...何とかならんもんか。

f:id:ts0818:20180916142035p:plain

保存されます。

f:id:ts0818:20180916142147p:plain

ダウンロードされました。

f:id:ts0818:20180916142439p:plain

 

JavaプロジェクトにJSON in Javaのライブラリを追加

とりあえず、Eclipseを起動して、「ファイル(F)」>「新規(N)」>「Java プロジェクト」。(ほんとなら、「パースペクティブ」を「Java EE」とかにして、「動的Webプロジェクト」とかを作成しないと、JSONデータとかも受け取れんと思われますが)

f:id:ts0818:20180916143412p:plain

「プロジェクト名(P):」を適当に付けて、「次へ(N) >」をクリック。

f:id:ts0818:20180916144014p:plain

「完了(F)」をクリック。

f:id:ts0818:20180916144217p:plain

Javaプロジェクトができました。

f:id:ts0818:20180916144527p:plain

ダウンロードしておいたライブラリを配置する用のディレクトリを作成しときます。

f:id:ts0818:20180916144733p:plain

プロジェクトディレクトリ(ここでは、「JsonTest」)の直下に、新たにフォルダーを追加。「フォルダー名(N):」は適当に。「完了(F)」をクリック。

f:id:ts0818:20180916144815p:plain

Eclipseに作成した「lib」ディレクトリに、ダウンロードしておいたライブラリをドラッグ&ドロップします。

f:id:ts0818:20180916145318p:plain

「ファイルをコピー(C)」にチェックされた状態で「OK」。

f:id:ts0818:20180916145231p:plain

そしたら、ライブラリをビルドパスに追加していきます。

www.hitachi.co.jp

⇧  上記サイト様を参考にさせていただきました。

「パッケージ・エクスプローラー」のなかのプロジェクトを選択した状態で、右クリックし「ビルド・パス(B)」>「外部アーカイブの追加(V)...」。

f:id:ts0818:20180916145702p:plain

先程、配置したライブラリを選択。

自分の場合ですと、「C:¥Eclipse4.6¥pleiades¥workspace05¥JsonTest¥lib¥json-20180813.jar」になります。

ご自分のライブラリを配置した場所に合わせてください。

f:id:ts0818:20180916150154p:plain

「参照ライブラリー」が追加され、その中にライブラリが追加されていればOKのようです。

f:id:ts0818:20180916151154p:plain

 

Javaで利用してみる

では、さっそく、Javaでクラスを作成して、ライブラリを使っていくということで。

まずは、クラスを作成。「新規(W)」>「クラス」をクリック。

f:id:ts0818:20180916151457p:plain

「パッケージ(K):」「名前(M):」を適当に入力し、「public static void main(String[] args)(V)」にチェックし、「完了(F)」をクリック。

f:id:ts0818:20180916151742p:plain

メインメソッドを持つクラスファイルが出来上がります。

 

今回は、外部から、

{
    "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形式のデータが送られてきた体で。

本当は、AngularJSとかのJavaScriptフレームワークと、Javaプロジェクトを連携させて、JSONデータの送受信したいんですが、調べる時間が無いですね(涙)

ja.infobyip.com

⇧  上記サイト様で、JSON形式のデータをエンコードしておきます。

f:id:ts0818:20180916164856p:plain

そしたらば、クラスを編集。

How to convert JSONObject to HashMap in Java - Quora

⇧  上記サイト様を参考、というかほぼまんま使わせていただきました。

package ts0818.json.test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class JsonTest {

  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}";
    // JSONデータをJSONオブジェクトに
    JSONObject jsonObject = new JSONObject(json);
    // JSONの順序は保証されないらしい...
    System.out.println(jsonObject);
    // JSONオブジェクトの値チェック
    Map<String, Object> resultMap = toMap(jsonObject);
    System.out.println(resultMap);
  }

  public static Map<String, Object> toMap(JSONObject object) throws JSONException {
    Map<String, Object> map = new HashMap<String, Object>();

    Iterator<String> keysItr = object.keys();
    while(keysItr.hasNext()) {
      String key = keysItr.next();
      Object value = object.get(key);

      // JSONオブジェクトの値が、配列の場合、
      if(value instanceof JSONArray) {
        value = toList((JSONArray) value);
        
      // JSONオブジェクトの値が、オブジェクトの場合
      } else if(value instanceof JSONObject) {
        value = toMap((JSONObject) value);
      }
      // NULLが含まれてる場合は、空文字に置き換え
      if(value == JSONObject.NULL) {
        value = "";
      }
      map.put(key, value);
    }
    return map;
  }

  public static List<Object> toList(JSONArray array) throws JSONException {
  
    List<Object> list = new ArrayList<Object>();
    
    for(int i = 0; i < array.length(); i++) {
    
      Object value = array.get(i);
      
      if(value instanceof JSONArray) {
        value = toList((JSONArray) value);
        
      } else if(value instanceof JSONObject) {
        value = toMap((JSONObject) value);
      }
      list.add(value);
    }
    return list;
  }

}

そしたらば、メインメソッドを持ったクラス(ここでは、「JsonTest.java」)を選択した状態で右クリックし、「実行(R)」>「3 Java アプリケーション」を選択。(このへんはEclipseのバージョンによって若干、名前とか変わってるかもです。)

f:id:ts0818:20180917142033p:plain

コンソールに結果が表示されます。

f:id:ts0818:20180916165033p:plain

JSONの順番は、むちゃくちゃですが、nullは、空文字に置き換わったようです。順番を保持する方法は分からずでした...。

 

注意する点としては、ライブラリとしてJSON in Java を利用する場合、

値がnullJSONは、JSONObject.NULLオブジェクトとして扱われます。

org.json.JSONObjectにおけるnullの取扱い | infoScoop開発者ブログ

⇧  上記サイト様でも仰っているように、

value == null    

のような、Java本来のnull判定では、NULLかどうかを判定できないということですかね。

そして、今回のJSONデータの場合だと、JSONArrayの場合の処理は必要なかったっぽいですね...。

本当は再帰処理は、パフォーマンス的にあまり推奨されないらしいんですが、うまい方法が見つからずですね...。 

 

そもそも、JSON in Javaライブラリだと、JSONを、Javaクラスのオブジェクトにマッピングして変換できないんじゃないか疑惑が...

java-study.blog.jp

⇧  上記サイト様によりますと、Apache CommonsのライブラリのBeanUtilsを使えば、MapのデータからJavaクラスのオブジェクトを生成したりできるらしいですが、JSONデータの構成が、複数のJavaクラスのオブジェクトを集めて成り立っているとしたら、上手くマッピングできない気が...

 

やはり、JacksonかGsonを使っていく感じになるんですかね。

 

Java、勉強せねばですが、どう勉強すれば良いのか分からずですね...。

 

今回はこのへんで。