Come analizzare JSON in Kotlin?


121

Sto ricevendo una stringa di oggetto JSON abbastanza profonda da un servizio che devo analizzare in un oggetto JSON e quindi mapparlo alle classi.

Come posso trasformare una stringa JSON in oggetto in Kotlin?

Dopo la mappatura alle rispettive classi, stavo usando StdDeserializer di Jackson. Il problema sorge nel momento in cui l'oggetto aveva proprietà che dovevano essere deserializzate in classi. Non sono riuscito a ottenere il mappatore di oggetti, almeno non sapevo come, all'interno di un altro deserializzatore.

Grazie in anticipo per qualsiasi aiuto. Preferibilmente, in modo nativo, sto cercando di ridurre il numero di dipendenze di cui ho bisogno, quindi se la risposta è solo per la manipolazione e l'analisi JSON sarebbe sufficiente.


2
Non ho sviluppato in Java. Non è un errore che ricevo. Semplicemente non so come eseguire un'analisi efficace in Kotlin in modo nativo. Tutte le ricerche portano sempre a un framework. Java ha un org.json.simple. Fidandosi delle funzionalità di completamento automatico dell'IDE, Kotlin no.
AJ_1310

Il pacchetto org.json.simple non è nativo di Java. Immagino sia questa libreria: github.com/fangyidong/json-simple . Puoi usarlo anche con Kotlin se vuoi (anche se la libreria klaxon suggerita da Jason Bourne potrebbe essere una scelta migliore per Kotlin).
marstran

Dai un'occhiata a github.com/square/moshi . C'è un post sul blog a riguardo su medium.com/square-corner-blog/…
James Moore,

Risposte:


72

Puoi usare questa libreria https://github.com/cbeust/klaxon

Klaxon è una libreria leggera per analizzare JSON in Kotlin.


86
Autore qui, non esitate a scrivermi se avete domande / suggerimenti.
Cedric Beust

Quali librerie Java devo importare per ottenere il parser corretto? Ho provato org.json. *, Ma devo mancare qualcosa nelle mie impostazioni Gradle. Penso che la documentazione di Klaxon presuma troppo (come l'utente che conosce le librerie Java).
Makis

@CedricBeust hai provato a lavorare con l'oggetto classe con sqlite? qualche pratica consigliata qui?
Raju yourPepe

104

Non c'è dubbio che il futuro dell'analisi in Kotlin sarà con kotlinx.serialization. Fa parte delle librerie Kotlin. È ancora al momento della scrittura in fase di incubazione.

https://github.com/Kotlin/kotlinx.serialization

import kotlinx.serialization.*
import kotlinx.serialization.json.JSON

@Serializable
data class MyModel(val a: Int, @Optional val b: String = "42")

fun main(args: Array<String>) {

    // serializing objects
    val jsonData = JSON.stringify(MyModel.serializer(), MyModel(42))
    println(jsonData) // {"a": 42, "b": "42"}

    // serializing lists
    val jsonList = JSON.stringify(MyModel.serializer().list, listOf(MyModel(42)))
    println(jsonList) // [{"a": 42, "b": "42"}]

    // parsing data back
    val obj = JSON.parse(MyModel.serializer(), """{"a":42}""")
    println(obj) // MyModel(a=42, b="42")
}

3
Il problema che ho con questo è che difficilmente puoi usarlo con i generici. Almeno non ho capito come farlo. E di certo non voglio usare la riflessione.
natronite

3
La serializzazione di KotlinX è ancora in fase sperimentale, quindi introducono modifiche sostanziali nelle nuove versioni. Inoltre, non è thread-safe, quindi il tuo JSON potrebbe essere danneggiato se diversi thread tentano di utilizzare una singola istanza di Json(è comune). Inoltre, ci sono diversi problemi Github aperti con l'etichetta del bug. Quindi, direi che è ancora rischioso per l'uso in produzione (a meno che tu non sia disposto a dedicare tempo alla risoluzione di potenziali problemi e non prevedi di aggiornarlo frequentemente). Il progetto è davvero interessante soprattutto per i progetti Kotlin Multiplatform, ma non è ancora stabile.
Javad Sadeqzadeh

Sembra che ci sia già un cambiamento radicale, JSON ora si chiama Json
xjcl

Ciò richiede anche una dipendenza aggiuntiva, quindi segui il link per una guida
xjcl

34

Senza libreria esterna (su Android)

Per analizzarlo:

val jsonString = """
    {
       "type":"Foo",
       "data":[
          {
             "id":1,
             "title":"Hello"
          },
          {
             "id":2,
             "title":"World"
          }
       ]
    }        
"""

Usa queste classi:

import org.json.JSONObject

class Response(json: String) : JSONObject(json) {
    val type: String? = this.optString("type")
    val data = this.optJSONArray("data")
            ?.let { 0.until(it.length()).map { i -> it.optJSONObject(i) } } // returns an array of JSONObject
            ?.map { Foo(it.toString()) } // transforms each JSONObject of the array into Foo
}

class Foo(json: String) : JSONObject(json) {
    val id = this.optInt("id")
    val title: String? = this.optString("title")
}

Uso:

val foos = Response(jsonString)

2
Quindi, se questo non richiede alcuna libreria esterna, ciò dovrebbe significare che org.json.JSONObject fa parte della libreria standard, giusto?
still_dreaming_1

@ still_dreaming_1 sì, "Aggiunto nel livello API 1", cfr. developer.android.com/reference/org/json/JSONObject.html
frouo

Immagino sia una cosa Android? Lo stavo cercando nella libreria standard Kotlin o Java per la JVM.
still_dreaming_1

Oh sì assolutamente, mi dispiace di aver dimenticato di menzionarlo nella risposta! Forse potresti usarlo JsonObjectse la tua JVM è in esecuzione sotto Java7 ( docs.oracle.com/javaee/7/api/javax/json/JsonObject.html )?
frouo

Oh non l'ho trovato prima, grazie! Ci sono anche altre classi correlate come JsonReader. Sembra che facciano parte di Java EE, ma non di Java SE. Cercherò di passare a Java EE.
still_dreaming_1

26

Puoi usare Gson.

Esempio

Passo 1

Aggiungi compile

compile 'com.google.code.gson:gson:2.8.2'

Passo 2

Converti json in Kotlin Bean(usa JsonToKotlinClass )

Come questo

Json dati

{
"timestamp": "2018-02-13 15:45:45",
"code": "OK",
"message": "user info",
"path": "/user/info",
"data": {
    "userId": 8,
    "avatar": "/uploads/image/20180115/1516009286213053126.jpeg",
    "nickname": "",
    "gender": 0,
    "birthday": 1525968000000,
    "age": 0,
    "province": "",
    "city": "",
    "district": "",
    "workStatus": "Student",
    "userType": 0
},
"errorDetail": null
}

Kotlin Bean

class MineUserEntity {

    data class MineUserInfo(
        val timestamp: String,
        val code: String,
        val message: String,
        val path: String,
        val data: Data,
        val errorDetail: Any
    )

    data class Data(
        val userId: Int,
        val avatar: String,
        val nickname: String,
        val gender: Int,
        val birthday: Long,
        val age: Int,
        val province: String,
        val city: String,
        val district: String,
        val workStatus: String,
        val userType: Int
    )
}

Passaggio 3

Uso Gson

var gson = Gson()
var mMineUserEntity = gson?.fromJson(response, MineUserEntity.MineUserInfo::class.java)

questo mi dà java.lang.IllegalStateException: era prevista una stringa ma era BEGIN_OBJECT alla riga 1 colonna 700 percorso
Srishti Roy

Dovresti prima controllare i tuoi dati di ritorno. @ SrishtiRoy
KeLiuyue

ha funzionato, ma se la mia risposta JSON è del tipo "categorie": ["Consigliato"], allora?
Srishti Roy

@SrishtiRoy la risposta è dati JSON illegali. I dati JSON legali iniziano con {o[
KeLiuyue

1
Il problema con Gson è che ignora il nullability. Una valproprietà può essere facilmente nullse non è presente in JSON. Inoltre, i valori predefiniti non vengono utilizzati affatto.
user3738870

21

Non sono sicuro che questo sia ciò di cui hai bisogno, ma è così che l'ho fatto.

Utilizzando import org.json.JSONObject:

    val jsonObj = JSONObject(json.substring(json.indexOf("{"), json.lastIndexOf("}") + 1))
    val foodJson = jsonObj.getJSONArray("Foods")
    for (i in 0..foodJson!!.length() - 1) {
        val categories = FoodCategoryObject()
        val name = foodJson.getJSONObject(i).getString("FoodName")
        categories.name = name
    }

Ecco un esempio del json:

{"Foods": [{"FoodName": "Apples", "Weight": "110"}]}


8
qual è la dipendenza?
Luís Soares

Ho usato org.json. Ecco il link: mvnrepository.com/artifact/org.json/json/20180813
markB

Questo metodo richiede che la classe abbia un costruttore predefinito senza alcun parametro. Che cosa succede se i dati di classe hanno params in costruttore come di seguito: data class SomeClass(val param1: Int, val param2: Int).
leimenghao

@leimenghao Puoi farlo in una riga: val categories = SomeClass (param1 = foodJson.getJSONObject (i) .getString ("FoodName"), param2 = foodJson.getJSONObject (i) .getInt ("Weight"))
markB

Funziona davvero bene. Solo per dire, puoi usare al for (i in 0 until foodJson!!.length()) {posto di for (i in 0..foodJson!!.length() - 1) {. Fa lo stesso, ed è molto più visivo
Arnyminer Z

12

Personalmente uso il modulo Jackson per Kotlin che puoi trovare qui: jackson-module-kotlin .

implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$version"

Ad esempio, ecco il codice per analizzare il JSON dello skilltree Path of Exile che è piuttosto pesante (84k righe quando formattato):

Codice Kotlin:

package util

import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.module.kotlin.*
import java.io.File

data class SkillTreeData( val characterData: Map<String, CharacterData>, val groups: Map<String, Group>, val root: Root,
                          val nodes: List<Node>, val extraImages: Map<String, ExtraImage>, val min_x: Double,
                          val min_y: Double, val max_x: Double, val max_y: Double,
                          val assets: Map<String, Map<String, String>>, val constants: Constants, val imageRoot: String,
                          val skillSprites: SkillSprites, val imageZoomLevels: List<Int> )


data class CharacterData( val base_str: Int, val base_dex: Int, val base_int: Int )

data class Group( val x: Double, val y: Double, val oo: Map<String, Boolean>?, val n: List<Int> )

data class Root( val g: Int, val o: Int, val oidx: Int, val sa: Int, val da: Int, val ia: Int, val out: List<Int> )

data class Node( val id: Int, val icon: String, val ks: Boolean, val not: Boolean, val dn: String, val m: Boolean,
                 val isJewelSocket: Boolean, val isMultipleChoice: Boolean, val isMultipleChoiceOption: Boolean,
                 val passivePointsGranted: Int, val flavourText: List<String>?, val ascendancyName: String?,
                 val isAscendancyStart: Boolean?, val reminderText: List<String>?, val spc: List<Int>, val sd: List<String>,
                 val g: Int, val o: Int, val oidx: Int, val sa: Int, val da: Int, val ia: Int, val out: List<Int> )

data class ExtraImage( val x: Double, val y: Double, val image: String )

data class Constants( val classes: Map<String, Int>, val characterAttributes: Map<String, Int>,
                      val PSSCentreInnerRadius: Int )

data class SubSpriteCoords( val x: Int, val y: Int, val w: Int, val h: Int )

data class Sprite( val filename: String, val coords: Map<String, SubSpriteCoords> )

data class SkillSprites( val normalActive: List<Sprite>, val notableActive: List<Sprite>,
                         val keystoneActive: List<Sprite>, val normalInactive: List<Sprite>,
                         val notableInactive: List<Sprite>, val keystoneInactive: List<Sprite>,
                         val mastery: List<Sprite> )

private fun convert( jsonFile: File ) {
    val mapper = jacksonObjectMapper()
    mapper.configure( DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true )

    val skillTreeData = mapper.readValue<SkillTreeData>( jsonFile )
    println("Conversion finished !")
}

fun main( args : Array<String> ) {
    val jsonFile: File = File( """rawSkilltree.json""" )
    convert( jsonFile )

JSON (non formattato): http://filebin.ca/3B3reNQf3KXJ/rawSkilltree.json

Data la tua descrizione, credo che corrisponda alle tue esigenze.


1
A differenza di Klaxon (aveva un bug quando ho provato), Jackson funziona davvero :)
redsk

Inoltre, puoi utilizzare il plug-in delle classi di dati da JSON a Kotlin in intellij per generare le classi di dati per te.
Brooks DuBois

7

Per convertire JSON in Kotlin usa http://www.json2kotlin.com/

Inoltre puoi usare il plugin Android Studio. File> Impostazioni, selezionare Pluginsnella struttura a sinistra, premere "Sfoglia repository ...", cercare " JsonToKotlinClass ", selezionarlo e fare clic sul pulsante verde "Installa".

collegare

Dopo il riavvio di AS puoi usarlo. Puoi creare una classe con File > New > JSON To Kotlin Class (JsonToKotlinClass). Un altro modo è premere Alt + K.

inserisci qui la descrizione dell'immagine

Quindi vedrai una finestra di dialogo per incollare JSON.

Nel 2018 ho dovuto aggiungere package com.my.package_name all'inizio di una lezione.


4

Prima di tutto.

Puoi utilizzare il plug-in del convertitore di classi di dati da JSON a Kotlin in Android Studio per il mapping JSON alle classi POJO (classe di dati kotlin). Questo plugin annoterà la tua classe di dati Kotlin in base a JSON.

Quindi puoi utilizzare il convertitore GSON per convertire JSON in Kotlin.

Segui questo tutorial completo: Kotlin Android JSON Parsing Tutorial

Se vuoi analizzare manualmente json.

val **sampleJson** = """
  [
  {
   "userId": 1,
   "id": 1,
   "title": "sunt aut facere repellat provident occaecati excepturi optio 
    reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita"
   }]
   """

Codice da analizzare sopra JSON Array e relativo oggetto all'indice 0.

var jsonArray = JSONArray(sampleJson)
for (jsonIndex in 0..(jsonArray.length() - 1)) {
Log.d("JSON", jsonArray.getJSONObject(jsonIndex).getString("title"))
}

1

http://www.jsonschema2pojo.org/ Ciao, puoi usare questo sito web per convertire json in pojo.
controllo + Alt + shift + k

Dopodiché puoi convertire manualmente quella classe del modello in una classe del modello kotlin. con l'aiuto della scorciatoia sopra.


1
Si convertirà in Java.
CoolMind

0

Un po 'tardi, ma qualunque cosa.

Se preferisci analizzare JSON a JavaScript come i costrutti che fanno uso della semantica di Kotlin, ti consiglio JSONKraken , di cui sono l'autore.

Suggerimenti e opinioni in merito sono molto apprezzati!


-4

Scarica la fonte di deme da qui ( analisi Json in Android Kotlin )

Aggiungi questa dipendenza:

compile 'com.squareup.okhttp3:okhttp:3.8.1'

Chiama la funzione API:

 fun run(url: String) {
    dialog.show()
    val request = Request.Builder()
            .url(url)
            .build()

    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            dialog.dismiss()

        }

        override fun onResponse(call: Call, response: Response) {
            var str_response = response.body()!!.string()
            val json_contact:JSONObject = JSONObject(str_response)

            var jsonarray_contacts:JSONArray= json_contact.getJSONArray("contacts")

            var i:Int = 0
            var size:Int = jsonarray_contacts.length()

            al_details= ArrayList();

            for (i in 0.. size-1) {
                var json_objectdetail:JSONObject=jsonarray_contacts.getJSONObject(i)


                var model:Model= Model();
                model.id=json_objectdetail.getString("id")
                model.name=json_objectdetail.getString("name")
                model.email=json_objectdetail.getString("email")
                model.address=json_objectdetail.getString("address")
                model.gender=json_objectdetail.getString("gender")

                al_details.add(model)


            }

            runOnUiThread {
                //stuff that updates ui
                val obj_adapter : CustomAdapter
                obj_adapter = CustomAdapter(applicationContext,al_details)
                lv_details.adapter=obj_adapter
            }

            dialog.dismiss()

        }

    })
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.