HOWTO · Java

Java で文字列を JSON オブジェクトに変換する

このチュートリアルでは、java で文字列を json オブジェクトに変換する方法を説明します。

このページの内容

Java で JSON テキストを使用するには、JSON ライブラリで解析します。解析すると、動的に調べることができる、ライブラリ固有の JSON ツリーが生成されます。一方、テキストを Java クラスにマッピングすると、型付きオブジェクトが生成されます。ここで紹介する 3 つの選択肢は org.json、Gson、Jackson です。それぞれに固有の依存関係が必要です。フィールドを動的に調べたい場合はツリーを選び、データの形が分かっていて型付きフィールドが役立つ場合は Java クラスへのマッピングを選んでください。

Java で文字列を JSON オブジェクトに変換するには JSONObject を使用する

JSON-java ライブラリ(org.json)は、new JSONObject(json) でオブジェクト文字列を解析します。Maven の場合は、pom.xml の <dependencies> 要素内に org.json:json:20201115 を追加します。コンストラクタは JSONObject を返します。文字列メンバーと整数メンバーは getString("name") と getInt("age") で読み取ります。これらの呼び出しは Java の値を返しますが、JSONObject 自体はライブラリのメモリ内 JSON オブジェクト表現のままです。オブジェクトメンバーの順序は信頼できる意味的な性質ではないため、メンバーはキーで取得してください。

以下の完全な例では、new JSONObject(json) を使用して共有サンプル {"name":"John","age":30} を解析し、両方のフィールドを読み取って出力し、JSONException を処理します。

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

public class StringToJsonObjectWithJsonOrg {
    public static void main(String[] args) {
        String json = "{\"name\":\"John\",\"age\":30}";
        try {
            JSONObject object = new JSONObject(json);
            System.out.println("name: " + object.getString("name"));
            System.out.println("age: " + object.getInt("age"));
        } catch (JSONException e) {
            System.err.println("Invalid JSON: " + e.getMessage());
        }
    }
}
name: John
age: 30

比較のため、JDK のみを使用する以下の例も、正規表現だけを使ってサンプルの 2 つのフィールドを抽出し、その値を出力します。この例はこの平坦な入力形状に限定されており、一般的な JSON を解析したり検証したりするものではありません。入力は main 内で宣言されているため、スニペットを StringToJsonObject.java として保存し、JDK でコンパイルしてください。

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringToJsonObject {
    public static void main(String[] args) {
        String json = "{\"name\":\"John\",\"age\":30}";
        Pattern field = Pattern.compile("\\\"(name|age)\\\"\\s*:\\s*(?:\\\"([^\\\"]*)\\\"|(\\d+))");
        Matcher matcher = field.matcher(json);
        Map<String, String> values = new LinkedHashMap<>();
        while (matcher.find()) {
            String value = matcher.group(2) != null ? matcher.group(2) : matcher.group(3);
            values.put(matcher.group(1), value);
        }
        System.out.println("name: " + values.get("name"));
        System.out.println("age: " + values.get("age"));
    }
}
name: John
age: 30

この正規表現は、表示されている文字列メンバーと整数メンバーだけを対象としています。JSON 構文を検証せず、エスケープされた引用符、入れ子構造、配列、その他の JSON 値も正しく処理しません。アプリケーションの入力には JSONObject または別のパーサーを使用し、必要なメンバーが存在し、期待される型であることを読み取る前に確認してください。解析と型付き読み取りの周囲で JSONException を捕捉し、呼び出し側が解析または値アクセスの失敗を報告したり、そこから回復したりできるようにしてください。

Java で文字列を JSON オブジェクトに変換するには Google Gson を使用する

Gson はテキストを JSON ツリーに解析することも、Java クラスに直接マッピングすることもできます。クラスを定義せずにフィールドを調べるには、Maven の <dependencies> 要素内に com.google.code.gson:gson:2.8.6 を追加します。JsonParser.parseString(json).getAsJsonObject() でツリーを解析し、object.get("name").getAsString() と object.get("age").getAsInt() でメンバーを読み取ります。これはドメインクラスのインスタンスではなく、JsonObject ツリーを返します。

以下の完全な例では、同じサンプルを JsonObject に解析し、両方のフィールドを読み取って出力し、解析エラーを処理します。

import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

public class StringToJsonObjectWithGson {
    public static void main(String[] args) {
        String json = "{\"name\":\"John\",\"age\":30}";
        try {
            JsonObject object = JsonParser.parseString(json).getAsJsonObject();
            System.out.println("name: " + object.get("name").getAsString());
            System.out.println("age: " + object.get("age").getAsInt());
        } catch (JsonParseException | IllegalStateException e) {
            System.err.println("Invalid JSON: " + e.getMessage());
        }
    }
}
name: John
age: 30

データの形が分かっていて型付きフィールドが役立つ場合は、String name フィールドと int age フィールドを持つ User クラスを定義し、new Gson().fromJson(json, User.class) を呼び出して User を生成します。そのインスタンスを JSON テキストにシリアル化して戻すには、new Gson().toJson(user) を呼び出します。GsonBuilder.setPrettyPrinting() はシリアル化時の書式を設定しますが、解析されたツリーや Java の値を変更するものではありません。解析の問題については JsonParseException を処理し、その後、必須フィールドを検証してください。有効な JSON でも、プログラムが必要とするデータが欠けていることがあります。

文字列を JSON オブジェクトに変換するには Jackson を使用する

Jackson のツリーモデルは、フィールドを動的に選択する必要がある場合に役立ちます。Maven の <dependencies> 要素内に com.fasterxml.jackson.core:jackson-databind:2.11.3 を追加します。jackson-databind アーティファクトは ObjectMapper ツリー API を提供し、依存関係として Jackson core を取り込みます。new ObjectMapper().readTree(json) を呼び出すと、テキストが Jackson の JSON ツリー表現である JsonNode に解析されます。サンプルの値は node.get("name").asText() と node.get("age").asInt() で読み取ります。各値には対応するメンバーを使用してください。

以下の完全な例では、ObjectMapper.readTree(json) を使用し、両方のフィールドを読み取って出力し、チェック例外である JsonProcessingException を処理します。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class StringToJsonObjectWithJackson {
    public static void main(String[] args) {
        String json = "{\"name\":\"John\",\"age\":30}";
        try {
            JsonNode node = new ObjectMapper().readTree(json);
            System.out.println("name: " + node.get("name").asText());
            System.out.println("age: " + node.get("age").asInt());
        } catch (JsonProcessingException e) {
            System.err.println("Invalid JSON: " + e.getMessage());
        }
    }
}
name: John
age: 30

readTree はチェック例外である JsonProcessingException をスローする可能性があるため、解析を行う場所で処理してください。また、asText() や asInt() のような便利なアクセサーを使用する前に、要求した各ノードが存在し、期待される型であることを確認してください。そうしないと、JSON ドキュメント自体は正常に解析できていても、欠けているメンバーや異なる型のメンバーから、アプリケーションに適さない値が生成されることがあります。

3 つのライブラリはいずれも JSON テキストを解析しますが、そのツリー型と API は異なります。JSONObject、JsonObject、JsonNode です。不正な形式のドキュメントは解析中に失敗し、例外のクラスとメッセージは選択したライブラリによって異なります。解析だけでは、必須フィールドが存在することや、値がアプリケーションが期待する型であることは保証されません。そのため、使用前にそれらを検証してください。JDK のみを使用する正規表現の例は、表示されている平坦なサンプルに限定されます。一般的な JSON 入力には、依存関係を伴うライブラリ API のいずれかを使用してください。