processing
Jackson

ProcessingでJSONをスマートに扱う

はじめに

この記事はProcessing Advent Calendar 2017 23日目の記事です。
埋まっていなくて流れてしまっていたので、書かせていただきます。

今回はProcessingでJSONを扱う方法の一つを書いていきます。
Processingには標準でJSONObjectJSONArrayというJSONを扱うクラスがあります。

公式リファレンスのサンプルではこんな風に使っています。

JSONObject json;

void setup() {

  json = loadJSONObject("data.json");

  int id = json.getInt("id");
  String species = json.getString("species");
  String name = json.getString("name");

  println(id + ", " + species + ", " + name);
}

WebAPIを叩く時もloadJSONObject("http://webapi.com/users")のように使用してJSONObjectを生成するのが普通ですが、いちいちjson.getString("name")とかでパースしてUserクラスなどに入れていくのも面倒です。
なので今回はJacksonというJSONを扱う為のライブラリを使います。

導入

まずはjarファイルをProcessingライブラリのフォーマットに整えます。
ダウンロードしてくるjarファイルはこれらです。

これらをダウンロードしたらPROCESSING_PATH/libraries以下に適当な名前のディレクトリを作ります。
自分はjacksonP5としました。Jackson 5みたいですね。
ディレクトリを作ったらその中にlibraryディレクトリを作って、先ほどのjarファイルを入れます。
こんな構造になります。

.
└── library
    ├── jackson-annotations-2.9.3.jar
    ├── jackson-core-2.9.3.jar
    └── jackson-databind-2.9.3.jar

これでライブラリの導入は完了です。
ここでProcessingを再起動しておきましょう。

サンプル

では早速JSON -> Javaオブジェクトのサンプルを書いていきます。
ここでの注意点はインスタンス化したいクラスは外部のJavaファイルで用意することです。
pdeファイル内に書くとJsonMappingExceptionが発生します。
おそらくPAppletを継承したメインクラスの内部クラスとして扱われるのが原因ではないかと思います。
Processing IDEからJavaファイルを作る方法は、New Tab(command + shift + N)で入力するファイル名の末尾に.javaと入力すれば生成されます。今回はUser.javaと入力します。

JacksonSample.pde
import com.fasterxml.jackson.databind.ObjectMapper;

String json = "{\"id\": 1, \"name\": \"太郎\", \"age\": 25, \"job\": \"エンジニア\"}";

void setup() {
  println(json);
  ObjectMapper mapper = new ObjectMapper();
  try {
    User user = mapper.readValue(json, User.class);
    System.out.println(user);
  }
  catch (IOException e) {
    println(e);
  }
}

void draw() {
}
User.java
import com.fasterxml.jackson.annotation.JsonProperty;

public class User {
  @JsonProperty("id")
  private int id;
  @JsonProperty("name")
  private String name;
  @JsonProperty("age")
  private int age;
  @JsonProperty("job")
  private String job;

  public String toString() {
    return String.format("ID %d番の%s %d歳です。職業は%sです。", id, name, age, job);
  }
}

実行したらこんな感じに出力されると思います。

{"id": 1, "name": "太郎", "age": 25, "job": "エンジニア"}
ID 1番の太郎 25歳です。職業はエンジニアです。

これでWeb APIリファレンスがあれば、いちいちパースするコードを書かなくて済みますね。

WebAPIを叩く

試しにjson-serverを使ってWebAPIを叩いてみます。
json-serverの使い方は以前にこちらの記事に書いたので見てみてください。
メインのソースコードをこんな風にすると、json-serverのID 0のユーザが取得できます。

JacksonSample.pde
import com.fasterxml.jackson.databind.ObjectMapper;
import http.requests.*;

String URL = "http://localhost:3000/users/1";

void setup() {
  GetRequest get = new GetRequest(URL);
  get.send();
  ObjectMapper mapper = new ObjectMapper();
  try {
    User user = mapper.readValue(get.getContent(), User.class);
    System.out.println(user);
  }
  catch (IOException e) {
    println(e);
  }
}

void draw() {
}

出力

ID 1番の二郎 30歳です。職業は魔法使いです。

配列を受け取る

http://localhost:3000/usersにリクエストして配列を受け取りたいときはTypeReferenceを使うみたいです。

JacksonSample.pde
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import http.requests.*;

String URL = "http://localhost:3000/users";

void setup() {
  GetRequest get = new GetRequest(URL);
  get.send();
  ObjectMapper mapper = new ObjectMapper();
  try {
    TypeReference typeRef = new TypeReference<ArrayList<User>>(){};
    ArrayList<User> users = mapper.readValue(get.getContent(), typeRef);
    for (User user : users) {
      System.out.println(user);
    }
  }
  catch (IOException e) {
    println(e);
  }
}

void draw() {
}

出力

ID 0番の太郎 30歳です。職業は勇者です。
ID 1番の二郎 30歳です。職業は魔法使いです。

ではでは