KotlinとKlaxsonによるJSONの処理
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]から入手できます。