1. 概要

この記事では、JerseyやJacksonなどのサードパーティの依存関係を使用せずに、コアJavaEEのみを使用してJSONを処理する方法を説明します。 使用するほとんどすべてがjavax.jsonパッケージによって提供されます。

2. JSONへのオブジェクトの書き込みString

JavaオブジェクトをJSONStringに変換するのは非常に簡単です。 単純なPersonクラスがあると仮定します。

public class Person {
    private String firstName;
    private String lastName;
    private Date birthdate;

    // getters and setters
}

そのクラスのインスタンスをJSONString に変換するには、最初に JsonObjectBuilder のインスタンスを作成し、 add()を使用してプロパティと値のペアを追加する必要があります。方法:

JsonObjectBuilder objectBuilder = Json.createObjectBuilder()
  .add("firstName", person.getFirstName())
  .add("lastName", person.getLastName())
  .add("birthdate", new SimpleDateFormat("DD/MM/YYYY")
  .format(person.getBirthdate()));

add()メソッドにはいくつかのオーバーロードされたバージョンがあることに注意してください。 2番目のパラメーターとして、ほとんどのプリミティブ型(およびボックス化されたオブジェクト)を受け取ることができます。

プロパティの設定が完了したら、オブジェクトをStringに書き込む必要があります。

JsonObject jsonObject = objectBuilder.build();
        
String jsonString;
try(Writer writer = new StringWriter()) {
    Json.createWriter(writer).write(jsonObject);
    jsonString = writer.toString();
}

以上です! 生成されたStringは次のようになります。

{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978"}

2.1. JsonArrayBuilderを使用してアレイを構築する

ここで、例をもう少し複雑にするために、 Person クラスが変更され、emailsという新しいプロパティが追加されたと仮定します。このプロパティには電子メールアドレスのリストが含まれます。

public class Person {
    private String firstName;
    private String lastName;
    private Date birthdate;
    private List<String> emails;
    
    // getters and setters

}

そのリストのすべての値をJsonObjectBuilderに追加するには、JsonArrayBuilderの助けが必要です。

JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
                
for(String email : person.getEmails()) {
    arrayBuilder.add(email);
}
        
objectBuilder.add("emails", arrayBuilder);

JsonArrayBuilderオブジェクトを2番目のパラメーターとして受け取るadd()メソッドのさらに別のオーバーロードバージョンを使用していることに注意してください。

それでは、2つの電子メールアドレスを持つPersonオブジェクトに対して生成された文字列を見てみましょう。

{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978",
 "emails":["[email protected]","[email protected]"]}

2.2. PRETTY_PRINTINGを使用した出力のフォーマット

これで、Javaオブジェクトを有効なJSON Stringに正常に変換できました。 次に、次のセクションに進む前に、出力をより「JSONのよう」で読みやすくするために、いくつかの簡単なフォーマットを追加しましょう。

前の例では、単純な Json.createWriter()静的メソッドを使用してJSONWriterを作成しました。 生成されたStringをより細かく制御するために、Java7のJsonWriterFactory 機能を利用して、特定の構成のライターを作成します。

Map<String, Boolean> config = new HashMap<>();

config.put(JsonGenerator.PRETTY_PRINTING, true);
        
JsonWriterFactory writerFactory = Json.createWriterFactory(config);
        
String jsonString;
 
try(Writer writer = new StringWriter()) {
    writerFactory.createWriter(writer).write(jsonObject);
    jsonString = writer.toString();
}

コードは少し冗長に見えるかもしれませんが、実際にはあまり効果がありません。

まず、 JsonWriterFactory のインスタンスを作成し、コンストラクターに構成マップを渡します。 マップには、PRETTY_PRINTINGプロパティにtrueを設定するエントリが1つだけ含まれています。 次に、 Json.createWriter()を使用する代わりに、そのファクトリインスタンスを使用してライターを作成します。

新しい出力には、JSON Stringを特徴付ける特徴的な改行と表が含まれます。

{
    "firstName":"Michael",
    "lastName":"Scott",
    "birthdate":"06/15/1978",
    "emails":[
        "[email protected]",
        "[email protected]"
    ]
}

3. 文字列からJavaオブジェクトを構築する

次に、反対の操作を実行します。JSONStringをJavaオブジェクトに変換します。

変換プロセスの主要部分は、JsonObjectを中心に展開されます。 このクラスのインスタンスを作成するには、静的メソッド Json.createReader()に続いて readObject()を使用します。

JsonReader reader = Json.createReader(new StringReader(jsonString));

JsonObject jsonObject = reader.readObject();

createReader()メソッドは、InputStreamをパラメーターとして受け取ります。 この例では、JSONが String オブジェクトに含まれているため、 StringReader、を使用していますが、これと同じメソッドを使用して、ファイルからコンテンツを読み取ることができます。 FileInputStreamを使用します。

JsonObject のインスタンスが手元にある場合、 getString()メソッドを使用してプロパティを読み取り、取得した値をPersonの新しく作成されたインスタンスに割り当てることができます。クラス:

Person person = new Person();

person.setFirstName(jsonObject.getString("firstName"));
person.setLastName(jsonObject.getString("lastName"));
person.setBirthdate(dateFormat.parse(jsonObject.getString("birthdate")));

3.1. JsonArrayを使用してList値を取得する

JsonObject からリスト値を抽出するには、JsonArrayという特別なクラスを使用する必要があります。

JsonArray emailsJson = jsonObject.getJsonArray("emails");

List<String> emails = new ArrayList<>();

for (JsonString j : emailsJson.getValuesAs(JsonString.class)) {
    emails.add(j.getString());
}

person.setEmails(emails);

それでおしまい! Json StringからPersonの完全なインスタンスを作成しました。

4. 値のクエリ

ここで、JSON String内にある非常に特定のデータに関心があると仮定します。

ペットショップのクライアントを表す以下のJSONについて考えてみます。 何らかの理由で、ペットリストから3番目のペットの名前を取得する必要があるとします。

{
    "ownerName": "Robert",
    "pets": [{
        "name": "Kitty",
        "type": "cat"
    }, {
        "name": "Rex",
        "type": "dog"
    }, {
        "name": "Jake",
        "type": "dog"
    }]
}

単一の値を取得するためだけにテキスト全体をJavaオブジェクトに変換することは、あまり効率的ではありません。 それでは、変換の試練全体を経ることなく、JSON Stringsをクエリするためのいくつかの戦略を確認しましょう。

4.1. オブジェクトモデルAPIを使用したクエリ

JSON構造内の既知の場所でプロパティの値を照会するのは簡単です。 前の例で使用したのと同じクラスのJsonObject、のインスタンスを使用できます。

JsonReader reader = Json.createReader(new StringReader(jsonString));

JsonObject jsonObject = reader.readObject();

String searchResult = jsonObject
  .getJsonArray("pets")
  .getJsonObject(2)
  .getString("name");

ここでの落とし穴は、 get *()メソッドの正しいシーケンスを使用してjsonObjectプロパティをナビゲートすることです。

この例では、最初に getJsonArray()を使用して「ペット」リストへの参照を取得します。これにより、3つのレコードを含むリストが返されます。 次に、 getJsonObject()メソッドを使用します。このメソッドは、パラメーターとしてインデックスを取り、リストの3番目の項目を表す別のJsonObjectを返します。 最後に、 getString()を使用して、探している文字列値を取得します。

4.2. ストリーミングAPIを使用したクエリ

JSON String で正確なクエリを実行する別の方法は、メインクラスとしてJsonParserを持つストリーミングAPIを使用することです。

JsonParser は、JSへの非常に高速な読み取り専用の転送アクセスを提供しますが、オブジェクトモデルよりもいくらか複雑であるという欠点があります。

JsonParser jsonParser = Json.createParser(new StringReader(jsonString));

int count = 0;
String result = null;

while(jsonParser.hasNext()) {
    Event e = jsonParser.next();
    
    if (e == Event.KEY_NAME) {
        if(jsonParser.getString().equals("name")) {
            jsonParser.next();
           
            if(++count == 3) {
                result = jsonParser.getString();
                break;
            }
        }   
    }
}

この例では、前の例と同じ結果が得られます。 petsリストの3番目のペットからnameを返します。

JsonParserJson.createParser()を使用して作成されたら、イテレーターを使用する必要があります(したがって、 JsonParser の「転送アクセス」の性質)。探している1つまたは複数のプロパティに到達するまでJSONトークンを介して。

イテレータをステップスルーするたびに、JSONデータの次のトークンに移動します。 したがって、現在のトークンが予期されたタイプであるかどうかを注意深く確認する必要があります。 これは、 next()呼び出しによって返されるEventをチェックすることによって行われます。

トークンにはさまざまな種類があります。 この例では、プロパティの名前を表す KEY_NAME タイプに関心があります(例: 「ownerName」、「pets」、「name」、「type」)。 値が「name」のKEY_NAMEトークンを3回ステップ実行すると、次のトークンにリストの3番目のペットの名前を表す文字列値が含まれることがわかります。

これは、特により複雑なJSON構造の場合、オブジェクトモデルAPIを使用するよりも明らかに困難です。 いつものように、どちらを選択するかは、扱う特定のシナリオによって異なります。

5. 結論

いくつかの簡単な例を使用して、JavaEEJSON処理APIに関する多くの根拠を説明しました。 JSON処理に関するその他の優れた機能については、一連のJacksonの記事を確認してください。

GitHubリポジトリで、この記事で使用されているクラスのソースコードといくつかの単体テストを確認してください。