動作確認環境

  • Java 16
  • Jackson 2.12.5

JsonNodeを取得する

path, get, at

keyが存在する場合

JsonNodeを取得するにはpath, get, atの3つの方法がある。

pathgetは今いるJSONのノード内のkeyを指定する。atは今いるJSONのノード内をルートとして/を使ったパスでkeyを指定する(常に/から開始する必要があり、/から開始しないとIllegalArgumentExceptionが発生する)。

java.lang.IllegalArgumentException: Invalid input: JSON Pointer expression must start with '/'`

具体的なコードを書くと以下のようになる。

// import static java.lang.System.out;

static ObjectMapper objectMapper = new ObjectMapper();

public static void main(String[] args) throws JsonProcessingException {
    String json = "{\"key\":\"value\"}";
    JsonNode node = objectMapper.readTree(json);

    JsonNode keyNodeByPath = node.path("key");
    JsonNode keyNodeByGet = node.get("key");
    JsonNode keyNodeByAt = node.at("/key");

    out.println(keyNodeByPath.equals(keyNodeByGet) && keyNodeByPath.equals(keyNodeByAt)); // true
}

keyが存在しない場合

先程のコードでは、path, get, atに実際に存在するkeyを指定した。存在しないkeyの場合はどうなるか。

public static void main(String[] args) throws JsonProcessingException {
    String json = "{\"key\":\"value\"}";
    JsonNode node = objectMapper.readTree(json);

    out.println(node.path("notExist").getClass()); // MissingNode
    out.println(node.get("notExist")); // null
    out.println(node.at("/notExist").getClass()); // MissingNode
}

pathatMissingNodeが返り、getnullが返る。

getNullPointerExceptionが発生しやすく使いづらいかもしれない。

JSONのvalueを取得する

textValue, asText

keyが存在し、valueの型が想定通りの場合

path等で目的のvalueがあるJsonNodeを取得した後は、そのJsonNodeのvalueを取得するために、valueが文字列であればtextValueasTextを実行する。

public static void main(String[] args) throws JsonProcessingException {
    String json = "{\"key\":\"value\"}";
    JsonNode node = objectMapper.readTree(json);

    out.println(node.path("key").textValue()); // value
    out.println(node.path("key").asText()); // value
}

keyが存在し、valueの型が想定通りでない場合

valueは今実際に文字列だが、想定では数字が返ってくるはずだとする。数字が返るはずなので、textValueではなくintValueasTextではなくasIntで実行する。

out.println(node.path("key").intValue()); // 0
out.println(node.path("key").asInt()); // 0

実際には数字に変換できない文字列が返ってきたので、0になってしまった。

他にも文字列だと想定していたのに、実際にはオブジェクトだとnullや空文字が返ってしまう。

out.println(node.textValue()); // null
out.println(node.asText()); // emtpy string

数値系(Int, Long, Double)と真偽(Boolean)はプリミティブ型がメソッドの返り値になり、valueの型が想定通りではない場合にはデフォルト値になるので、~Valueas~も同じ結果になる。

文字列だけはnullが返るか空文字が返るかの違いがある。(メソッドの返り値がStringでプリミティブ型じゃないのが原因だから???)

もし文字列の場合にasTextでもnullを返したければasText(String defaultValue)を使うといい。

out.println(node.path("notExist").textValue()); // null
out.println(node.path("notExist").asText(null)); // null

as~にはデフォルト値を指定できるメソッドがオーバーロードされている。

keyが存在しない場合

keyが存在しない場合は、path等で取得したJsonNodeがMissingNodeになる。これに対してtextValue等を実行した結果は、「keyが存在し、valueの型が想定通りでない場合」と同じになる。

keyが存在するが、valueがnullの場合

valueがnullの場合、asTextでは文字列として"null"が返ってきてしまうため注意が必要。

public static void main(String[] args) throws JsonProcessingException {
    String json = "{\"key\":null}";
    JsonNode node = objectMapper.readTree(json);

    out.println(node.path("key").textValue()); // null
    // textValueで取得するとnullになる
    out.println(node.path("key").textValue() == null); // true

    out.println(node.path("key").asText()); // null
    // asTextで取得すると "null" になる
    out.println(Objects.equals(node.path("key").asText(), "null")); // true
}

intValue, asIntではとも0になる。これについては先ほど見た通り「プリミティブ型がメソッドの返り値になり、valueの型が想定通りではない場合にはデフォルト値になるので、~Valueもas~も同じ結果」である。

as~は種類が少ないのに対して~Valueは種類が多い

他の違いとしてはas~は種類が少ないのに対して~Valueは種類が多い。