¿Cómo analizar JSON en Kotlin?

121

Recibo una cadena de objeto JSON bastante profunda de un servicio que debo analizar en un objeto JSON y luego asignarlo a las clases.

¿Cómo puedo transformar una cadena JSON en un objeto en Kotlin?

Después de eso, el mapeo a las clases respectivas, estaba usando StdDeserializer de Jackson. El problema surge en el momento en que el objeto tenía propiedades que también debían ser deserializadas en clases. No pude obtener el mapeador de objetos, al menos no sabía cómo, dentro de otro deserializador.

Gracias de antemano por cualquier ayuda. Preferiblemente, de forma nativa, estoy tratando de reducir la cantidad de dependencias que necesito, por lo que si la respuesta es solo para la manipulación y el análisis de JSON, sería suficiente.

AJ_1310
fuente
2
No he desarrollado en Java. No es un error que obtengo. Simplemente no sé cómo hacer un análisis efectivo en Kotlin de forma nativa. Todas las búsquedas siempre conducen a un marco. Java tiene un org.json.simple. Confiando en las funciones de autocompletar del IDE, Kotlin no lo hace.
AJ_1310
El paquete org.json.simple no es nativo de Java. Supongo que es esta biblioteca: github.com/fangyidong/json-simple . También puede usarlo con Kotlin si lo desea (aunque la biblioteca klaxon que sugirió Jason Bourne podría ser una mejor opción para Kotlin).
Marstran
Eche un vistazo a github.com/square/moshi . Hay una publicación de blog al respecto en medium.com/square-corner-blog/…
James Moore

Respuestas:

72

Puede utilizar esta biblioteca https://github.com/cbeust/klaxon

Klaxon es una biblioteca ligera para analizar JSON en Kotlin.

Istiak Morsalin
fuente
86
Autor aquí, no dude en enviarme un correo electrónico si tiene preguntas / sugerencias.
Cedric Beust
¿Qué bibliotecas de Java tengo que importar para obtener el analizador correcto? Probé org.json. *, Pero debe faltar algo en mi configuración de Gradle. Creo que la documentación de Klaxon asume demasiado (como si el usuario conociera las bibliotecas de Java).
Makis
@CedricBeust ¿Ha intentado trabajar con el objeto de clase con sqlite? ¿Alguna práctica recomendada aquí?
Raju yourPepe
104

No hay duda de que el futuro del análisis sintáctico en Kotlin será con kotlinx.serialization. Es parte de las bibliotecas de Kotlin. Todavía se encuentra en el momento de escribir este artículo en etapa de incubadora.

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")
}
Eliseo Sterngold
fuente
3
El problema que tengo con esto es que difícilmente se puede usar con genéricos. Al menos no he descubierto cómo hacerlo. Y ciertamente no quiero usar la reflexión.
natronita
3
La serialización de KotlinX aún se encuentra en fase experimental, por lo que introducen cambios importantes en las nuevas versiones. Además, no es seguro para subprocesos, por lo que su JSON podría dañarse si varios subprocesos intentan usar una sola instancia de Json(es común). Además, hay varios problemas abiertos de Github con la etiqueta del error. Por lo tanto, diría que su uso en producción sigue siendo riesgoso (a menos que esté dispuesto a dedicar tiempo a solucionar problemas potenciales y no planee actualizarlo con frecuencia). El proyecto es realmente interesante, especialmente para los proyectos multiplataforma de Kotlin, pero aún no es estable.
Javad Sadeqzadeh
Parece que ya hay un cambio importante, JSON ahora se llama Json
xjcl
Esto también requiere una dependencia adicional, así que siga el enlace para obtener una guía
xjcl
34

Sin biblioteca externa (en Android)

Para analizar esto:

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

Utilice estas clases:

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)
frouo
fuente
2
Entonces, si esto no requiere bibliotecas externas, eso debería significar que org.json.JSONObject es parte de la biblioteca estándar, ¿verdad?
still_dreaming_1
@ still_dreaming_1 sí lo es, "Agregado en el nivel de API 1", cf. developer.android.com/reference/org/json/JSONObject.html
frouo
¿Supongo que es una cosa de Android? Lo estaba buscando en la biblioteca estándar de Kotlin o Java para la JVM.
still_dreaming_1
Oh, sí, absolutamente, ¡lo siento, olvidé mencionar eso en la respuesta! ¿Quizás podría usar JsonObjectsi su JVM se ejecuta en Java7 ( docs.oracle.com/javaee/7/api/javax/json/JsonObject.html )?
viernes
¡Oh, no encontré ese antes, gracias! También hay otras clases relacionadas como JsonReader. Parece que son parte de Java EE, pero no de Java SE. Examinaré la posibilidad de cambiar a Java EE.
still_dreaming_1
26

Puede utilizar Gson.

Ejemplo

Paso 1

Agregar compilar

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

Paso 2

Convierta json a Kotlin Bean(use JsonToKotlinClass )

Me gusta esto

Json datos

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

Paso 3

Utilizar Gson

var gson = Gson()
var mMineUserEntity = gson?.fromJson(response, MineUserEntity.MineUserInfo::class.java)
KeLiuyue
fuente
esto me da java.lang.IllegalStateException: se esperaba una cadena pero estaba BEGIN_OBJECT en la línea 1, columna 700, ruta
Srishti Roy
Primero debe verificar sus datos de devolución. @ SrishtiRoy
KeLiuyue
funcionó, pero si mi respuesta json es como "categorías": ["Recomendado"], ¿entonces?
Srishti Roy
@SrishtiRoy, la respuesta son datos JSON ilegales. Los datos JSON legales se inician con {o[
KeLiuyue
1
El problema con Gson es que ignora la nulabilidad. Una valpropiedad puede ser fácilmente nullsi falta en el JSON. Además, los valores predeterminados no se utilizan en absoluto.
user3738870
21

No estoy seguro si esto es lo que necesita, pero así es como lo hice.

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

Aquí hay una muestra del json:

{"Alimentos": [{"Nombre de alimentos": "Manzanas", "Peso": "110"}]}

markB
fuente
8
cual es la dependencia?
Luís Soares
Usé org.json. Aquí está el enlace: mvnrepository.com/artifact/org.json/json/20180813
markB
Este método requiere que la clase tenga un constructor predeterminado sin ningún parámetro. ¿Qué pasa si la clase de datos tiene parámetros en el constructor como se muestra a continuación data class SomeClass(val param1: Int, val param2: Int)?
leimenghao
@leimenghao Puedes hacer esto en una línea: val categorías = SomeClass (param1 = foodJson.getJSONObject (i) .getString ("FoodName"), param2 = foodJson.getJSONObject (i) .getInt ("Peso"))
markB
Funciona muy bien. Solo para decir, puedes usar en for (i in 0 until foodJson!!.length()) {lugar de for (i in 0..foodJson!!.length() - 1) {. Hace lo mismo, y es bastante más visual
Arnyminer Z
12

Yo personalmente uso el módulo Jackson para Kotlin que puedes encontrar aquí: jackson-module-kotlin .

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

Como ejemplo, aquí está el código para analizar el JSON del árbol de habilidades Path of Exile que es bastante pesado (84k líneas cuando se formatea):

Código 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 (sin formato): http://filebin.ca/3B3reNQf3KXJ/rawSkilltree.json

Dada su descripción, creo que coincide con sus necesidades.

Neurf
fuente
1
A diferencia de Klaxon (tenía un error cuando lo intenté), Jackson realmente funciona :)
redsk
Además, puede usar el complemento de clase de datos JSON a Kotlin en intellij para generar las clases de datos por usted.
Brooks DuBois
7

Para convertir JSON a Kotlin, use http://www.json2kotlin.com/

También puede usar el complemento de Android Studio. Archivo> Configuración, seleccione Pluginsen el árbol de la izquierda, presione "Examinar repositorios ...", busque " JsonToKotlinClass ", selecciónelo y haga clic en el botón verde "Instalar".

enchufar

Después de reiniciar AS, puede usarlo. Puedes crear una clase con File > New > JSON To Kotlin Class (JsonToKotlinClass). Otra forma es presionar Alt + K.

ingrese la descripción de la imagen aquí

Luego verá un cuadro de diálogo para pegar JSON.

En 2018 tuve que agregar package com.my.package_nameal comienzo de una clase.

CoolMind
fuente
4

Ante todo.

Puede usar el complemento de conversión de clase de JSON a Kotlin Data en Android Studio para el mapeo JSON a clases POJO (clase de datos kotlin). Este complemento anotará su clase de datos Kotlin de acuerdo con JSON.

Luego puede usar el convertidor GSON para convertir JSON a Kotlin.

Siga este tutorial completo: Kotlin Android JSON Parsing Tutorial

Si desea analizar json manualmente.

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

Código para analizar por encima de JSON Array y su objeto en el índice 0.

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

http://www.jsonschema2pojo.org/ Hola, puede utilizar este sitio web para convertir json a pojo.
control + Alt + Mayús + k

Después de eso, puede convertir manualmente esa clase de modelo en la clase de modelo kotlin. con la ayuda del atajo anterior.

kundan kamal
fuente
1
Se convertirá a Java.
CoolMind
0

Un poco tarde, pero como sea.

Si prefiere analizar JSON a JavaScript como construcciones que utilizan la semántica de Kotlin, le recomiendo JSONKraken , del cual soy el autor.

¡Las sugerencias y opiniones al respecto son muy apreciadas!

Facundo garcia
fuente
-4

Descargue la fuente de deme desde aquí ( análisis de Json en android kotlin )

Agregue esta dependencia:

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

Llamar a la función 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()

        }

    })
Deepshikha Puri
fuente