1. 序章

JavaScript Object Notation(JSON)は、近年、データ交換形式として多くの人気を博しています。 Jsoniter は、他の利用可能なパーサーよりも柔軟でパフォーマンスの高いJSON解析を提供することを目的とした新しいJSON解析ライブラリです。

このチュートリアルでは、JavaのJsoniterライブラリを使用してJSONオブジェクトを解析する方法を説明します。

2. 依存関係

Jsoniter の最新バージョンは、MavenCentralリポジトリーから見つけることができます。

pom.xmlに依存関係を追加することから始めましょう。

<dependency>
    <groupId>com.jsoniter<groupId> 
    <artifactId>jsoniter</artifactId>
    <version>0.9.23</version>
</dependency>

同様に、build.gradleファイルに依存関係を追加できます。

compile group: 'com.jsoniter', name: 'jsoniter', version: '0.9.23'

3. Jsoniterを使用したJSON解析

Jsoniterは、JSONドキュメントを解析するための3つのAPIを提供します。

  • バインドAPI
  • 任意のAPI
  • イテレータAPI

上記の各APIを見てみましょう。

3.1. BindAPIを使用したJSON解析

bind APIは、JSONドキュメントをJavaクラスにバインドする従来の方法を使用します。

学生の詳細を含むJSONドキュメントについて考えてみましょう。

{"id":1,"name":{"firstName":"Joe","surname":"Blogg"}}

次に、上記のJSONを表すStudentおよびNameschemaクラスを定義しましょう。

public class Student {
    private int id;
    private Name name;
    
    // standard setters and getters
}
public class Name {
    private String firstName;
    private String surname;
    
    // standard setters and getters
}

バインドAPIを使用してJSONをJavaオブジェクトに逆シリアル化するのは非常に簡単です。 JsonIteratordeserializeメソッドを使用します。

@Test
public void whenParsedUsingBindAPI_thenConvertedToJavaObjectCorrectly() {
    String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
    
    Student student = JsonIterator.deserialize(input, Student.class);

    assertThat(student.getId()).isEqualTo(1);
    assertThat(student.getName().getFirstName()).isEqualTo("Joe");
    assertThat(student.getName().getSurname()).isEqualTo("Blogg");
}

The 学生スキーマクラスは id int データ・タイプただし、受け取ったJSONにの値 id 数字の代わりに? 例えば:

{"id":"1","name":{"firstName":"Joe","surname":"Blogg"}}

今回は、JSONの idが文字列値“ 1”であることに注意してください。 Jsoniterは、このシナリオに対処するために多分デコーダーを提供します。

3.2. たぶんデコーダー

JSON要素のデータ型があいまいな場合にJsoniterの多分デコーダーが便利です student.id フィールドのデータ型はあいまいです— Stringまたはintのいずれかです。 これを処理するには、 MaybeStringIntDecoder を使用して、スキーマクラスのidフィールドに注釈を付ける必要があります。

public class Student {
    @JsonProperty(decoder = MaybeStringIntDecoder.class)
    private int id;
    private Name name;
    
    // standard setters and getters
}

idの値がStringの場合でも、JSONを解析できるようになりました。

@Test
public void givenTypeInJsonFuzzy_whenFieldIsMaybeDecoded_thenFieldParsedCorrectly() {
    String input = "{\"id\":\"1\",\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
    
    Student student = JsonIterator.deserialize(input, Student.class);

    assertThat(student.getId()).isEqualTo(1); 
}

同様に、Jsoniterは、MaybeStringLongDecoderMaybeEmptyArrayDecoderなどの他のデコーダーを提供します。

ここで、 Student の詳細を含むJSONドキュメントを受け取ることを期待していたが、代わりに次のドキュメントを受け取ると想像してみましょう。

{"error":404,"description":"Student record not found"}

ここで何が起こったのですか? Student データで成功応答を期待していましたが、エラー応答を受け取りました。 これは非常に一般的なシナリオですが、これをどのように処理しますか?

1つの方法は、 null チェックを実行して、Studentデータを抽出する前にエラー応答を受信したかどうかを確認することです。 ただし、 null チェックを行うと、コードが読みづらくなる可能性があり、マルチレベルのネストされたJSONがある場合は問題がさらに悪化します。

AnyAPIを使用したJsoniterの解析が役に立ちます。

3.3. AnyAPIを使用したJSON解析

JSON構造自体が動的である場合、JSONのスキーマレス解析を提供するJsoniterのAnyAPIを使用できます。 これは、JSONを解析して地図

以前と同じようにStudent JSONを解析しますが、今回は AnyAPIを使用します。

@Test
public void whenParsedUsingAnyAPI_thenFieldValueCanBeExtractedUsingTheFieldName() {
    String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
    
    Any any = JsonIterator.deserialize(input);

    assertThat(any.toInt("id")).isEqualTo(1);
    assertThat(any.toString("name", "firstName")).isEqualTo("Joe");
    assertThat(any.toString("name", "surname")).isEqualTo("Blogg"); 
}

この例を理解しましょう。 まず、 JsonIterator.deserialize(..)を使用してJSONを解析します。 ただし、この場合、スキーマクラスは指定しません。 結果はタイプですどれでも。

次に、フィールド名を使用してフィールド値を読み取ります。 Any.toIntメソッドを使用して「id」フィールド値を読み取ります。 toInt メソッドは、「id」値を整数に変換します。 同様に、 toString メソッドを使用して、「name.firstName」および「name.surname」フィールド値を文字列値として読み取ります。

Any APIを使用して、要素がJSONに存在するかどうかを確認することもできます。要素を検索し、検索結果のvalueTypeを調べることでこれを行うことができます。 要素がJSONに存在しない場合、valueTypeINVALIDになります。

例えば:

@Test
public void whenParsedUsingAnyAPI_thenFieldValueTypeIsCorrect() {
    String input = "{\"id\":1,\"name\":{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}}";
    
    Any any = JsonIterator.deserialize(input);

    assertThat(any.get("id").valueType()).isEqualTo(ValueType.NUMBER);
    assertThat(any.get("name").valueType()).isEqualTo(ValueType.OBJECT);
    assertThat(any.get("error").valueType()).isEqualTo(ValueType.INVALID);
}

「id」フィールドと「name」フィールドはJSONに存在するため、それらのvalueTypeはそれぞれNUMBEROBJECTです。 ただし、JSON入力には「error」という名前の要素がないため、valueTypeINVALIDです。

前のセクションの最後で説明したシナリオに戻ると、受け取ったJSON入力が成功かエラー応答かを検出する必要があります。 「error」要素のvalueTypeを調べることにより、エラー応答を受信したかどうかを確認できます。

String input = "{\"error\":404,\"description\":\"Student record not found\"}";
Any response = JsonIterator.deserialize(input);

if (response.get("error").valueType() != ValueType.INVALID) {
    return "Error!! Error code is " + response.toInt("error");
}
return "Success!! Student id is " + response.toInt("id");

実行すると、上記のコードは “エラー!! エラーコードは404インチです

次に、IteratorAPIを使用してJSONドキュメントを解析する方法を見ていきます。

3.4. IteratorAPIを使用したJSON解析

バインディングを手動で実行する場合は、JsoniterのIteratorAPIを使用できます。JSONについて考えてみましょう。

{"firstName":"Joe","surname":"Blogg"}

以前に使用したNameスキーマクラスを使用して、 IteratorAPIを使用してJSONを解析します。

@Test
public void whenParsedUsingIteratorAPI_thenFieldValuesExtractedCorrectly() throws Exception {
    Name name = new Name();    
    String input = "{\"firstName\":\"Joe\",\"surname\":\"Blogg\"}";
    JsonIterator iterator = JsonIterator.parse(input);

    for (String field = iterator.readObject(); field != null; field = iterator.readObject()) {
        switch (field) {
            case "firstName":
                if (iterator.whatIsNext() == ValueType.STRING) {
                    name.setFirstName(iterator.readString());
                }
                continue;
            case "surname":
                if (iterator.whatIsNext() == ValueType.STRING) {
                    name.setSurname(iterator.readString());
                }
                continue;
            default:
                iterator.skip();
        }
    }

    assertThat(name.getFirstName()).isEqualTo("Joe");
    assertThat(name.getSurname()).isEqualTo("Blogg");
}

上記の例を理解しましょう。 まず、JSONドキュメントをイテレータとして解析します。 結果のJsonIteratorインスタンスを使用して、JSON要素を反復処理します。

  1. まず、 readObject メソッドを呼び出して、次のフィールド名を返します(または、ドキュメントの最後に達した場合は null )。
  2. フィールド名に関心がない場合は、skipメソッドを使用してJSON要素をスキップします。 それ以外の場合は、whatIsNextメソッドを使用して要素のデータ型を検査します。 whatIsNext メソッドの呼び出しは必須ではありませんが、フィールドのデータ型が不明な場合に役立ちます。
  3. 最後に、readStringメソッドを使用してJSON要素の値を抽出します。

4. 結論

この記事では、JSONドキュメントをJavaオブジェクトとして解析するためにJsoniterが提供するさまざまなアプローチについて説明しました。

最初に、スキーマクラスを使用してJSONドキュメントを解析する標準的な方法を確認しました。

次に、MaybeデコーダーとAnyデータ型をそれぞれ使用してJSONドキュメントを解析する際のファジーデータ型と動的構造の処理について説明しました。

最後に、JSONをJavaオブジェクトに手動でバインドするための IteratorAPIを確認しました。

いつものようにソース この記事で使用されている例のコードは、GitHubから入手できます。