Elaborazione di JSON con Kotlin e Klaxson

1. Panoramica

Klaxon è una delle librerie open source che possiamo utilizzare per analizzare JSON in Kotlin.

In questo tutorial, esamineremo le sue caratteristiche.

2. Dipendenza da Maven

Innanzitutto, dovremo aggiungere la dipendenza della libreria al nostro progetto Maven:

 com.beust klaxon 3.0.4 

L'ultima versione può essere trovata su jcenter o in Spring Plugins Repository.

3. Funzionalità API

Klaxon ha quattro API per lavorare con i documenti JSON. Li esploreremo nelle sezioni seguenti.

4. API Object Binding

Con questa API, possiamo associare documenti JSON a oggetti Kotlin e viceversa .

Per iniziare, definiamo il seguente documento JSON:

{ "name": "HDD" }

Successivamente, creeremo la classe Product per l'associazione:

class Product(val name: String)

Ora possiamo testare la serializzazione:

@Test fun givenProduct_whenSerialize_thenGetJsonString() { val product = Product("HDD") val result = Klaxon().toJsonString(product) assertThat(result).isEqualTo("""{"name" : "HDD"}""") }

E possiamo testare la deserializzazione:

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

Questa API supporta anche il lavoro con classi di dati e classi mutabili e immutabili.

Klaxon ci consente di personalizzare il processo di mappatura con l' annotazione @Json . Questa annotazione ha due proprietà:

  • nome - per impostare un nome diverso per i campi
  • ignorato - per ignorare i campi del processo di mappatura

Creiamo una classe CustomProduct per vedere come funzionano:

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

Ora, verificiamolo con un test:

@Test fun givenCustomProduct_whenSerialize_thenGetJsonString() { val product = CustomProduct("HDD", 1) val result = Klaxon().toJsonString(product) assertThat(result).isEqualTo("""{"productName" : "HDD"}""") }

Come possiamo vedere, la proprietà name viene serializzata come productName e la proprietà id viene ignorata.

5. Streaming API

Con l'API di streaming, possiamo gestire enormi documenti JSON leggendo da un flusso. Questa funzione consente al nostro codice di elaborare i valori JSON mentre sta ancora leggendo .

Dobbiamo utilizzare la classe JsonReader dall'API per leggere un flusso JSON. Questa classe ha due funzioni speciali per gestire lo streaming:

  • beginObject () - si assicura che il token successivo sia l'inizio di un oggetto
  • beginArray () - si assicura che il prossimo token sia l'inizio di un array

Con queste funzioni, possiamo essere sicuri che il flusso sia posizionato correttamente e che venga chiuso dopo aver consumato l'oggetto o l'array.

Testiamo l'API di streaming su un array della seguente classe ProductData :

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 JSON Path Query

Klaxon supporta la funzione di posizione dell'elemento dalla specifica del percorso JSON. Con questa API, possiamo definire corrispondenze di percorso per individuare voci specifiche nei nostri documenti .

Tieni presente che anche questa API è in streaming e riceveremo una notifica dopo che un elemento viene trovato e analizzato.

Dobbiamo usare l' interfaccia PathMatcher . Questa interfaccia viene chiamata quando il percorso JSON trova corrispondenze dell'espressione regolare.

Per usarlo, dobbiamo implementare i suoi metodi:

  • pathMatches () - restituisce true se vogliamo osservare questo percorso
  • onMatch () - attivato quando viene trovato il percorso; nota che il valore può essere solo un tipo di base (ad esempio, int , String ) e mai JsonObject o JsonArray

Facciamo un test per vederlo in azione.

Innanzitutto, definiamo un documento JSON di inventario come fonte di dati:

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

Ora implementiamo l' interfaccia PathMatcher come segue:

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

Nota che abbiamo definito la regex in modo che corrisponda al tipo di disco del nostro documento di inventario.

Ora siamo pronti per definire il nostro test:

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

7. API di basso livello

Con Klaxon, possiamo elaborare documenti JSON come una mappa o un elenco. Per fare ciò, possiamo utilizzare le classi JsonObject e JsonArray dall'API.

Facciamo un test per vedere JsonObject in azione:

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

Ora, facciamo un test per vedere la funzionalità 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) }) }

Come possiamo vedere in entrambi i casi, abbiamo effettuato le conversioni senza la definizione di classi specifiche.

8. Conclusione

In questo articolo, abbiamo esplorato la libreria Klaxon e le sue API per gestire i documenti JSON.

Come sempre, il codice sorgente è disponibile su Github.