1概要


Klaxon

は、JSONを解析するために使用できるオープンソースライブラリの1つです。/kotlin[Kotlin]。

このチュートリアルでは、その機能について見ていきます。


2 Mavenの依存関係

まず、Mavenプロジェクトにライブラリの依存関係を追加する必要があります。

<dependency>
    <groupId>com.beust</groupId>
    <artifactId>klaxon</artifactId>
    <version>3.0.4</version>
</dependency>

最新版はhttps://bintray.com/cbeust/maven/klaxon[jcenter]またはhttp://repo.spring.io/plugins-release/com/beust/klaxon/[Spring Plugins Repositoryにあります。]。


3 APIの機能

KlaxonにはJSONドキュメントを扱うための4つのAPIがあります。次のセクションでこれらを探ります。


4オブジェクトバインディングAPI

このAPIを使えば、JSONドキュメントをKotlinオブジェクトにバインドできます。はじめに、次のJSONドキュメントを定義しましょう。

{
    "name": "HDD"
}

次に、バインド用の

Product

クラスを作成します。

class Product(val name: String)

これで、直列化をテストできます。

@Test
fun givenProduct__whenSerialize__thenGetJsonString() {
    val product = Product("HDD")
    val result = Klaxon().toJsonString(product)

    assertThat(result).isEqualTo("""{"name" : "HDD"}""")
}

そして、デシリアライゼーションをテストできます。

@Test
fun givenJsonString__whenDeserialize__thenGetProduct() {
    val result = Klaxon().parse<Product>(
    """
        {
            "name" : "RAM"
        }
    """)

    assertThat(result?.name).isEqualTo("RAM")
}

このAPIは、データクラス、および可変クラスと不変クラスの処理もサポートしています。

Klaxonでは、

@ Json

アノテーションを使って

マッピングプロセスをカスタマイズすることができます

。この注釈には2つのプロパティがあります。


  • name

    – フィールドに異なる名前を設定します


  • ignored

    – マッピングプロセスのフィールドを無視します

これらがどのように機能するかを確認するために

CustomProduct

クラスを作成しましょう。

class CustomProduct(
    @Json(name = "productName")
    val name: String,
    @Json(ignored = true)
    val id: Int)

それでは、テストで検証しましょう。

@Test
fun givenCustomProduct__whenSerialize__thenGetJsonString() {
    val product = CustomProduct("HDD", 1)
    val result = Klaxon().toJsonString(product)

    assertThat(result).isEqualTo("""{"productName" : "HDD"}""")
}

ご覧のとおり、

name

プロパティは

productName

としてシリアル化され、

id

プロパティは無視されます。


5ストリーミングAPI

ストリーミングAPIを使用すると、ストリームから読み取ることで巨大なJSON文書を処理できます。この機能により、読み取り中にコードでJSON値を処理できます。

JSONストリームを読み込むには、APIの

JsonReader

クラスを使用する必要があります。このクラスには、ストリーミングを処理するための2つの特別な機能があります。


  • beginObject()

    – 次のトークンがの先頭であることを確認します

オブジェクト
**

beginArray()

– 次のトークンが最初のトークンであることを確認します

アレイ

これらの関数を使用すると、ストリームが正しく配置されていること、およびオブジェクトまたは配列を消費した後にストリームが閉じていることを確認できます。

次の

ProductData

クラスの配列に対してストリーミングAPIをテストしましょう。

data class ProductData(val name: String, val capacityInGb: Int)

@Test
fun givenJsonArray__whenStreaming__thenGetProductArray() {
    val jsonArray = """
   [        { "name" : "HDD", "capacityInGb" : 512 },
        { "name" : "RAM", "capacityInGb" : 16 }
   ]"""
    val expectedArray = arrayListOf(
      ProductData("HDD", 512),
      ProductData("RAM", 16))
    val klaxon = Klaxon()
    val productArray = arrayListOf<ProductData>()
    JsonReader(StringReader(jsonArray)).use {
        reader -> reader.beginArray {
            while (reader.hasNext()) {
                val product = klaxon.parse<ProductData>(reader)
                productArray.add(product!!)
            }
        }
    }

    assertThat(productArray).hasSize(2).isEqualTo(expectedArray)
}


6. JSONパスクエリーAPI

KlaxonはJSONパス仕様の要素配置機能をサポートしています。このAPIを使うと、ドキュメント内の特定のエントリを見つけるためのパスマッチャーを定義できます。

このAPIもストリーミングされていることに注意してください。要素が見つかって解析された後に通知されます。


PathMatcher

インターフェースを使用する必要があります。このインターフェースは、JSONパスが正規表現の一致を見つけたときに呼び出されます。

これを使用するには、そのメソッドを実装する必要があります。


  • pathMatches()

    – このパスを監視する場合はtrueを返します


  • onMatch()

    – パスが見つかったときに発生します。値は

基本型(

int



String

など)のみを使用し、

JsonObject



JsonArray

は使用しないでください。

実際に動作するかどうかテストしましょう。

まず、インベントリJSONドキュメントをデータのソースとして定義しましょう。

{
    "inventory" : {
        "disks" :[            {
                "type" : "HDD",
                "sizeInGb" : 1000
            },
            {
                "type" : "SDD",
                "sizeInGb" : 512
            }
       ]    }
}

今度は、

PathMatcher

インターフェースを次のように実装します。

val pathMatcher = object : PathMatcher {
    override fun pathMatches(path: String)
      = Pattern.matches(".** inventory.** disks.** type.** ", path)

    override fun onMatch(path: String, value: Any) {
        when (path) {
            "$.inventory.disks[0].type"
              -> assertThat(value).isEqualTo("HDD")
            "$.inventory.disks[1].type"
              -> assertThat(value).isEqualTo("SDD")
        }
    }
}

インベントリ文書のディスクの種類と一致するように正規表現を定義したことに注意してください。

これで、テストを定義する準備が整いました。

@Test
fun givenDiskInventory__whenRegexMatches__thenGetTypes() {
    val jsonString = """..."""
    val pathMatcher =//...
    Klaxon().pathMatcher(pathMatcher)
      .parseJsonObject(StringReader(jsonString))
}


7. 低レベルAPI

Klaxonでは、

Map



ListなどのJSONドキュメントを処理できます。これを行うには、APIの

JsonObject

クラスと

JsonArray__クラスを使用できます。


JsonObject

が実際に動作していることを確認するためのテストを行います。

@Test
fun givenJsonString__whenParser__thenGetJsonObject() {
    val jsonString = StringBuilder("""
        {
            "name" : "HDD",
            "capacityInGb" : 512,
            "sizeInInch" : 2.5
        }
    """)
    val parser = Parser()
    val json = parser.parse(jsonString) as JsonObject

    assertThat(json)
      .hasSize(3)
      .containsEntry("name", "HDD")
      .containsEntry("capacityInGb", 512)
      .containsEntry("sizeInInch", 2.5)
}

それでは、

JsonArray

機能を確認するためのテストを行いましょう。

@Test
fun givenJsonStringArray__whenParser__thenGetJsonArray() {
    val jsonString = StringBuilder("""
   [        { "name" : "SDD" },
        { "madeIn" : "Taiwan" },
        { "warrantyInYears" : 5 }
   ]""")
    val parser = Parser()
    val json = parser.parse(jsonString) as JsonArray<JsonObject>

    assertSoftly({
        softly ->
            softly.assertThat(json).hasSize(3)
            softly.assertThat(json[0]["name"]).isEqualTo("SDD")
            softly.assertThat(json[1]["madeIn"]).isEqualTo("Taiwan")
            softly.assertThat(json[2]["warrantyInYears"]).isEqualTo(5)
    })
}

どちらの場合でもわかるように、特定のクラスを定義せずに変換を行いました。


8結論

この記事では、JSON文書を処理するためのKlaxonライブラリーとそのAPIについて調べました。

いつものように、ソースコードはhttps://github.com/eugenp/tutorials/tree/master/kotlin-libraries[over Github]から入手できます。