Traitement de JSON avec Kotlin et Klaxson

Traitement de JSON avec Kotlin et Klaxson

1. Vue d'ensemble

Klaxon est l'une des bibliothèques open source que nous pouvons utiliser pour analyser JSON dansKotlin.

Dans ce didacticiel, nous allons examiner ses fonctionnalités.

2. Dépendance Maven

Premièrement, nous devrons ajouter la dépendance de la bibliothèque à notre projet Maven:


    com.beust
    klaxon
    3.0.4

La dernière version peut être trouvée dansjcenter ou dans lesSpring Plugins Repository.

3. Fonctionnalités de l'API

Klaxon dispose de quatre API pour travailler avec des documents JSON. Nous les explorerons dans les sections suivantes.

4. API de liaison d'objets

Avec cette API,we can bind JSON documents to Kotlin objects and vice-versa. Pour commencer, définissons le document JSON suivant:

{
    "name": "HDD"
}

Ensuite, nous allons créer la classeProduct pour la liaison:

class Product(val name: String)

Maintenant, nous pouvons tester la sérialisation:

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

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

Et nous pouvons tester la désérialisation:

@Test
fun givenJsonString_whenDeserialize_thenGetProduct() {
    val result = Klaxon().parse(
    """
        {
            "name" : "RAM"
        }
    """)

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

Cette API prend également en charge l'utilisation de classes de données, ainsi que de classes mutables et immuables.

Klaxon nous permet decustomize the mapping process with the @Json annotation. Cette annotation a deux propriétés:

  • name - pour définir un nom différent pour les champs

  • ignored - pour ignorer les champs du processus de mappage

Créons une classeCustomProduct pour voir comment cela fonctionne:

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

Maintenant, vérifions-le avec un test:

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

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

Comme nous pouvons le voir, la propriéténame est sérialisée en tant queproductName, et la propriétéid est ignorée.

5. API de streaming

Avec l'API Streaming, nous pouvons gérer d'énormes documents JSON en lisant un flux. Cette fonctionallows our code to process JSON values while it is still reading.

Nous devons utiliser la classeJsonReader de l'API pour lire un flux JSON. Cette classe a deux fonctions spéciales pour gérer le streaming:

  • beginObject() - s'assure que le prochain jeton est le début d'un objet

  • beginArray() - s'assure que le prochain jeton est le début d'un tableau

Avec ces fonctions, nous pouvons être sûrs que le flux est correctement positionné et qu’il est fermé après avoir consommé l’objet ou le tableau.

Testons l'API de streaming par rapport à un tableau de la classeProductData suivante:

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()
    JsonReader(StringReader(jsonArray)).use {
        reader -> reader.beginArray {
            while (reader.hasNext()) {
                val product = klaxon.parse(reader)
                productArray.add(product!!)
            }
        }
    }

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

6. API de requête de chemin JSON

Klaxon prend en charge la fonctionnalité d'emplacement d'élément à partir de la spécification du chemin JSON. Avec cette API, nous pouvonsdefine path matchers to locate specific entries in our documents.

Notez que cette API est également diffusée en continu et que nous serons avertis lorsqu'un élément sera trouvé et analysé.

Nous devons utiliser l'interfacePathMatcher. Cette interface est appelée lorsque le chemin JSON trouvé correspond à l'expression régulière.

Pour utiliser cela, nous devons implémenter ses méthodes:

  • pathMatches() - renvoie true si nous voulons observer ce chemin

  • onMatch() - déclenché lorsque le chemin est trouvé; notez que la valeur ne peut être qu'un type de base (par exemple,int,String) et jamaisJsonObject ouJsonArray

Faisons un test pour le voir en action.

Tout d'abord, définissons un document JSON d'inventaire comme source de données:

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

Maintenant, nous implémentons l'interfacePathMatcher comme suit:

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")
        }
    }
}

Notez que nous avons défini l'expression régulière correspondant au type de disque de notre document d'inventaire.

Maintenant, nous sommes prêts à définir notre test:

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

7. API de bas niveau

Avec Klaxon, nous pouvons traiter des documents JSON comme unMap ou unList. Pour ce faire, nous pouvons utiliser les classesJsonObject etJsonArray de l'API.

Faisons un test pour voir lesJsonObject en action:

@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)
}

Maintenant, faisons un test pour voir la fonctionnalité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

    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)
    })
}

Comme nous pouvons le constater dans les deux cas, nous avons effectué les conversions sans définir de classes spécifiques.

8. Conclusion

Dans cet article, nous avons exploré la bibliothèque Klaxon et ses API pour gérer les documents JSON.

Comme toujours, le code source est disponibleover on Github.