Comment analyser JSON dans Kotlin?


121

Je reçois une chaîne d'objet JSON assez profonde d'un service que je dois analyser en un objet JSON, puis la mapper à des classes.

Comment puis-je transformer une chaîne JSON en objet dans Kotlin?

Après cela, le mappage aux classes respectives, j'utilisais StdDeserializer de Jackson. Le problème se pose au moment où l'objet avait des propriétés qui devaient également être désérialisées en classes. Je n'ai pas pu obtenir le mappeur d'objet, du moins je ne savais pas comment, dans un autre désérialiseur.

Merci d'avance pour votre aide. De préférence, de manière native, j'essaie de réduire le nombre de dépendances dont j'ai besoin, donc si la réponse est uniquement pour la manipulation et l'analyse JSON, cela suffirait.


2
Je n'ai pas développé en Java. Ce n'est pas une erreur que j'obtiens. Je ne sais tout simplement pas comment effectuer une analyse syntaxique efficace dans Kotlin de manière native. Toutes les recherches mènent toujours à un cadre. Java a un org.json.simple. En faisant confiance aux fonctionnalités de saisie semi-automatique de l'EDI, Kotlin ne le fait pas.
AJ_1310

Le package org.json.simple n'est pas natif de Java. Je suppose que c'est cette bibliothèque: github.com/fangyidong/json-simple . Vous pouvez également l'utiliser avec Kotlin si vous le souhaitez (bien que la bibliothèque klaxon suggérée par Jason Bourne puisse être un meilleur choix pour Kotlin).
marstran

Jetez un œil à github.com/square/moshi . Il y a un article de blog à ce sujet sur medium.com/square-corner-blog/…
James Moore

Réponses:


72

Vous pouvez utiliser cette bibliothèque https://github.com/cbeust/klaxon

Klaxon est une bibliothèque légère pour analyser JSON dans Kotlin.


86
Auteur ici, n'hésitez pas à m'envoyer un email si vous avez des questions / suggestions.
Cedric Beust

Quelles bibliothèques Java dois-je importer pour obtenir le bon analyseur? J'ai essayé org.json. *, Mais je dois manquer quelque chose dans mes paramètres Gradle. Je pense que la documentation Klaxon en suppose trop (comme un utilisateur connaissant les bibliothèques Java).
Makis

@CedricBeust avez-vous essayé de travailler avec l'objet de classe avec sqlite? une pratique recommandée ici?
Raju yourPepe

104

Il ne fait aucun doute que l'avenir de l'analyse syntaxique dans Kotlin sera avec kotlinx.serialization. Il fait partie des bibliothèques Kotlin. Il est encore au moment de la rédaction au stade de l'incubateur.

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
Le problème que j'ai avec ceci est que vous pouvez difficilement l'utiliser avec des génériques. Au moins, je n'ai pas compris comment faire. Et je ne veux certainement pas utiliser la réflexion.
natronite

3
La sérialisation de KotlinX est encore en phase expérimentale, ils introduisent donc des changements de rupture dans les nouvelles versions. De plus, ce n'est pas thread-safe, donc votre JSON peut être corrompu si plusieurs threads essaient d'utiliser une seule instance de Json(c'est courant). De plus, il existe plusieurs problèmes Github ouverts avec l'étiquette de bogue. Donc, c'est toujours risqué pour une utilisation en production, je dirais (à moins que vous ne soyez prêt à passer du temps à résoudre des problèmes potentiels et que vous ne prévoyez pas de le mettre à jour fréquemment). Le projet est en effet intéressant surtout pour les projets Kotlin Multiplatform, mais il n'est pas encore stable.
Javad Sadeqzadeh

Il semble déjà y avoir un changement de rupture, JSON s'appelle maintenant Json
xjcl

Cela nécessite également une dépendance supplémentaire, alors suivez le lien pour un guide
xjcl

34

Sans bibliothèque externe (sur Android)

Pour analyser ceci:

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

Utilisez ces classes:

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

Usage:

val foos = Response(jsonString)

2
Donc, si cela ne nécessite aucune bibliothèque externe, cela devrait signifier que org.json.JSONObject fait partie de la bibliothèque standard, n'est-ce pas?
still_dreaming_1

@ still_dreaming_1 oui ça l'est, "Ajouté dans l'API niveau 1", cf. developer.android.com/reference/org/json/JSONObject.html
frouo

Je suppose que c'est un truc Android? Je le cherchais dans la bibliothèque standard Kotlin ou Java pour la JVM.
still_dreaming_1

Oh oui absolument, je suis désolé d'avoir oublié de le mentionner dans la réponse! Peut-être pourriez-vous l'utiliser JsonObjectsi votre JVM fonctionne sous Java7 ( docs.oracle.com/javaee/7/api/javax/json/JsonObject.html )?
frouo

Oh, je n'ai pas trouvé celui-là avant, merci! Il existe également d'autres classes connexes comme JsonReader. Il semble qu'ils font partie de Java EE, mais pas de Java SE. Je vais examiner la possibilité de passer à Java EE.
still_dreaming_1

26

Vous pouvez utiliser Gson.

Exemple

Étape 1

Ajouter une compilation

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

Étape 2

Convertir json en Kotlin Bean(utilisez JsonToKotlinClass )

Comme ça

Json Les données

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

Étape 3

Utilisation Gson

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

cela me donne java.lang.IllegalStateException: Attendu une chaîne mais BEGIN_OBJECT à la ligne 1 colonne 700 chemin
Srishti Roy

Vous devriez d'abord vérifier vos données de retour. @ SrishtiRoy
KeLiuyue

cela a fonctionné, mais si ma réponse json est comme "catégories": ["recommandé"], alors?
Srishti Roy

@SrishtiRoy la réponse est des données JSON illégales. Les données JSON légales sont démarrées avec {ou[
KeLiuyue

1
Le problème avec Gson est qu'il ne tient pas compte de la nullité. Une valpropriété peut facilement l'être nullsi elle est absente du JSON. De plus, les valeurs par défaut ne sont pas du tout utilisées.
user3738870

21

Je ne sais pas si c'est ce dont vous avez besoin, mais c'est ainsi que je l'ai fait.

Utilisation de 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
    }

Voici un exemple du json:

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


8
quelle est la dépendance?
Luís Soares

J'ai utilisé org.json. Voici le lien: mvnrepository.com/artifact/org.json/json/20180813
markB

Cette méthode nécessite que la classe ait un constructeur par défaut sans aucun paramètre. Que faire si la classe de données ont params dans le constructeur comme ci - dessous: data class SomeClass(val param1: Int, val param2: Int).
leimenghao

@leimenghao Vous pouvez le faire en une seule ligne: val categories = SomeClass (param1 = foodJson.getJSONObject (i) .getString ("FoodName"), param2 = foodJson.getJSONObject (i) .getInt ("Weight"))
markB

Fonctionne vraiment bien. Juste pour dire, vous pouvez utiliser à la for (i in 0 until foodJson!!.length()) {place de for (i in 0..foodJson!!.length() - 1) {. Il fait la même chose, et c'est beaucoup plus visuel
Arnyminer Z

12

J'utilise personnellement le module Jackson pour Kotlin que vous pouvez trouver ici: jackson-module-kotlin .

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

A titre d'exemple, voici le code pour analyser le JSON de l'arbre de compétences Path of Exile qui est assez lourd (84k lignes une fois formaté):

Code 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 formaté): http://filebin.ca/3B3reNQf3KXJ/rawSkilltree.json

Compte tenu de votre description, je pense que cela correspond à vos besoins.


1
Contrairement à Klaxon (il y avait un bug quand j'ai essayé), Jackson fonctionne réellement :)
redsk

De plus, vous pouvez utiliser le plugin de classe de données JSON vers Kotlin dans intellij pour générer les classes de données pour vous.
Brooks DuBois

7

Pour convertir JSON en Kotlin, utilisez http://www.json2kotlin.com/

Vous pouvez également utiliser le plugin Android Studio. Fichier> Paramètres, sélectionnez Pluginsdans l'arborescence de gauche, appuyez sur "Parcourir les référentiels ...", recherchez " JsonToKotlinClass ", sélectionnez-le et cliquez sur le bouton vert "Installer".

brancher

Après le redémarrage AS, vous pouvez l'utiliser. Vous pouvez créer une classe avec File > New > JSON To Kotlin Class (JsonToKotlinClass). Une autre façon consiste à appuyer sur Alt + K.

entrez la description de l'image ici

Ensuite, vous verrez une boîte de dialogue pour coller JSON.

En 2018, j'ai dû ajouter package com.my.package_nameau début d'un cours.


4

Tout d'abord.

Vous pouvez utiliser le plug-in de conversion de classe JSON vers Kotlin Data dans Android Studio pour le mappage JSON vers les classes POJO (classe de données kotlin). Ce plugin annotera votre classe de données Kotlin selon JSON.

Ensuite, vous pouvez utiliser le convertisseur GSON pour convertir JSON en Kotlin.

Suivez ce tutoriel complet: Tutoriel d'analyse Kotlin Android JSON

Si vous souhaitez analyser json manuellement.

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

Code à analyser au-dessus du tableau JSON et de son objet à l'index 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/ Salut, vous pouvez utiliser ce site Web pour convertir json en pojo.
Ctrl + Alt + Maj + k

Après cela, vous pouvez convertir manuellement cette classe de modèle en classe de modèle Kotlin. à l'aide du raccourci ci-dessus.


1
Il se convertira en Java.
CoolMind

0

Un peu tard, mais peu importe.

Si vous préférez analyser JSON à JavaScript comme des constructions utilisant la sémantique de Kotlin, je recommande JSONKraken , dont je suis l'auteur.

Les suggestions et opinions sur le sujet sont très appréciées!


-4

Téléchargez la source de deme à partir d'ici ( analyse Json dans Android Kotlin )

Ajoutez cette dépendance:

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

Appeler la fonction 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()

        }

    })
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.