Como analisar JSON em Kotlin?

122

Estou recebendo uma string de objeto JSON bastante profunda de um serviço que devo analisar para um objeto JSON e, em seguida, mapeá-lo para classes.

Como posso transformar uma string JSON em objeto em Kotlin?

Depois disso o mapeamento para as respectivas classes, usei o StdDeserializer da Jackson. O problema surge no momento em que o objeto tinha propriedades que também precisavam ser desserializadas em classes. Não consegui colocar o mapeador de objetos, pelo menos não sabia como, dentro de outro desserializador.

Agradecemos antecipadamente por qualquer ajuda. De preferência, nativamente, estou tentando reduzir o número de dependências de que preciso, então, se a resposta for apenas para manipulação e análise JSON, seria o suficiente.

AJ_1310
fonte
2
Não desenvolvi em Java. Não é um erro que estou recebendo. Só não sei como fazer uma análise eficaz em Kotlin nativamente. Todas as pesquisas sempre levam a uma estrutura. Java tem um org.json.simple. Confiando nos recursos de preenchimento automático do IDE, o Kotlin não.
AJ_1310
O pacote org.json.simple não é nativo do Java. Acho que é esta biblioteca: github.com/fangyidong/json-simple . Você também pode usá-lo com o Kotlin, se quiser (embora a biblioteca klaxon que Jason Bourne sugeriu possa ser uma escolha melhor para o Kotlin).
marstran
Dê uma olhada em github.com/square/moshi . Há uma postagem no blog sobre isso em medium.com/square-corner-blog/…
James Moore

Respostas:

73

Você pode usar esta biblioteca https://github.com/cbeust/klaxon

Klaxon é uma biblioteca leve para analisar JSON em Kotlin.

Istiak Morsalin
fonte
86
Autor aqui, sinta-se à vontade para me enviar um e-mail se tiver dúvidas / sugestões.
Cedric Beust
Quais bibliotecas java devo importar para obter o analisador correto? Tentei org.json. *, Mas devo estar faltando alguma coisa nas configurações do Gradle. Acho que a documentação do Klaxon pressupõe demais (como o usuário conhecer as bibliotecas Java).
Makis
@CedricBeust você tentou trabalhar com o objeto de classe com sqlite? alguma prática recomendada aqui?
Raju yourPepe
105

Não há dúvida de que o futuro da análise em Kotlin será com kotlinx.serialization. Faz parte das bibliotecas Kotlin. Ainda está em fase de incubação.

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")
}
Elisha Sterngold
fonte
3
O problema que tenho com isso é que você dificilmente pode usá-lo com genéricos. Pelo menos não descobri como fazer isso. E certamente não quero usar reflexão.
natronite
3
A serialização do KotlinX ainda está em fase experimental, então eles apresentam mudanças importantes em novos lançamentos. Além disso, não é seguro para thread, então seu JSON pode ser corrompido se vários threads tentarem usar uma única instância de Json(é comum). Além disso, existem vários problemas abertos no Github com o rótulo do bug. Portanto, ainda é arriscado para uso em produção, eu diria (a menos que você esteja disposto a gastar tempo corrigindo problemas em potencial e não planeje atualizá-lo com frequência). O projeto é realmente interessante, especialmente para projetos de Multiplataforma Kotlin, mas ainda não é estável.
Javad Sadeqzadeh
Parece já haver uma alteração significativa, JSON agora é chamado de Json
xjcl
Isso também requer uma dependência adicional, então siga o link para um guia
xjcl
34

Sem biblioteca externa (no Android)

Para analisar isso:

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

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

Uso:

val foos = Response(jsonString)
frouo
fonte
2
Portanto, se isso não requer nenhuma biblioteca externa, isso significa que org.json.JSONObject faz parte da biblioteca padrão, certo?
still_dreaming_1
@ still_dreaming_1 sim, é, "Adicionado na API nível 1", cf. developer.android.com/reference/org/json/JSONObject.html
frouo
Eu acho que é uma coisa do Android? Eu estava procurando por ele na biblioteca padrão Kotlin ou Java para o JVM.
still_dreaming_1
Ah, sim, com certeza, lamento ter esquecido de mencionar isso na resposta! Talvez você pudesse usar JsonObjectse o seu JVM estiver executando em Java7 ( docs.oracle.com/javaee/7/api/javax/json/JsonObject.html )?
partir de
Oh, eu não encontrei esse antes, obrigado! Existem algumas outras classes relacionadas também como JsonReader. Parece que fazem parte do Java EE, mas não do Java SE. Vou estudar a possibilidade de mudar para Java EE.
still_dreaming_1
26

Você pode usar Gson.

Exemplo

Passo 1

Adicionar compilar

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

Passo 2

Converter json para Kotlin Bean(usar JsonToKotlinClass )

Como isso

Json dados

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

etapa 3

Usar Gson

var gson = Gson()
var mMineUserEntity = gson?.fromJson(response, MineUserEntity.MineUserInfo::class.java)
KeLiuyue
fonte
isso me dá java.lang.IllegalStateException: Esperava-se uma string, mas era BEGIN_OBJECT na linha 1, coluna 700, caminho
Srishti Roy
Você deve verificar seus dados de retorno primeiro. @ SrishtiRoy
KeLiuyue
funcionou, mas se minha resposta json for como "categorias": ["Recomendado"], então?
Srishti Roy
@SrishtiRoy a resposta é dados JSON ilegais. Os dados JSON legais são iniciados com {ou[
KeLiuyue
1
O problema com Gson é que ele desconsidera a nulidade. Uma valpropriedade pode ser facilmente nullse estiver faltando no JSON. Além disso, os valores padrão não são usados.
user3738870
21

Não tenho certeza se é isso que você precisa, mas é assim que eu fiz.

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
    }

Aqui está um exemplo do json:

{"Alimentos": [{"FoodName": "Maçãs", "Peso": "110"}]}

markB
fonte
8
qual é a dependência?
Luís Soares
Eu usei org.json. Aqui está o link: mvnrepository.com/artifact/org.json/json/20180813
markB
Este método requer que a classe tenha um construtor padrão sem parâmetros. E se classe de dados têm parâmetros no construtor, como a seguir: data class SomeClass(val param1: Int, val param2: Int).
leimenghao 01 de
@leimenghao Você pode fazer isso em uma linha: val categorias = SomeClass (param1 = foodJson.getJSONObject (i) .getString ("FoodName"), param2 = foodJson.getJSONObject (i) .getInt ("Weight"))
markB
Funciona muito bem. Só para dizer, você pode usar em for (i in 0 until foodJson!!.length()) {vez de for (i in 0..foodJson!!.length() - 1) {. Ele faz o mesmo e é bem mais visual
Arnyminer Z
12

Eu pessoalmente uso o módulo Jackson para Kotlin que você pode encontrar aqui: jackson-module-kotlin .

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

Por exemplo, aqui está o código para analisar o JSON da árvore de habilidades do Path of Exile, que é bastante pesado (84k linhas quando formatado):

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 (não formatado): http://filebin.ca/3B3reNQf3KXJ/rawSkilltree.json

Dada a sua descrição, acredito que corresponda às suas necessidades.

Neurf
fonte
1
Diferentemente do Klaxon (tinha um bug quando tentei), Jackson realmente funciona :)
redsk
Além disso, você pode usar o JSON para o plugin de classe de dados Kotlin no intellij para gerar as classes de dados para você.
Brooks DuBois
7

Para converter JSON em Kotlin, use http://www.json2kotlin.com/

Além disso, você pode usar o plugin Android Studio. Arquivo> Configurações, selecione Pluginsna árvore à esquerda, pressione "Navegar nos repositórios ...", pesquise " JsonToKotlinClass ", selecione-o e clique no botão verde "Instalar".

plugar

Após a reinicialização do AS, você pode usá-lo. Você pode criar uma classe com File > New > JSON To Kotlin Class (JsonToKotlinClass). Outra maneira é pressionar Alt + K.

insira a descrição da imagem aqui

Em seguida, você verá uma caixa de diálogo para colar JSON.

Em 2018 tive que adicionar package com.my.package_nameno início de uma aula.

CoolMind
fonte
4

Em primeiro lugar.

Você pode usar o plug-in conversor de classe JSON para Kotlin Data no Android Studio para mapeamento JSON para classes POJO (classe de dados kotlin). Este plugin irá anotar sua classe de dados Kotlin de acordo com JSON.

Depois, você pode usar o conversor GSON para converter JSON em Kotlin.

Siga este tutorial completo: Tutorial de análise JSON do Kotlin Android

Se você deseja analisar 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 a ser analisado acima da matriz JSON e seu objeto no índice 0.

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

http://www.jsonschema2pojo.org/ Olá, você pode usar este site para converter json para pojo.
control + Alt + shift + k

Depois disso, você pode converter manualmente essa classe de modelo em classe de modelo kotlin. com a ajuda do atalho acima.

kundan kamal
fonte
1
Ele será convertido para Java.
CoolMind
0

Um pouco tarde, mas tanto faz.

Se você preferir analisar JSON em vez de JavaScript, como construções que usam a semântica Kotlin, recomendo JSONKraken , do qual sou o autor.

Agradecemos as sugestões e opiniões sobre o assunto!

Facundo Garcia
fonte
-4

Baixe o código-fonte do deme aqui ( análise Json em android kotlin )

Adicione esta dependência:

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

Função API de chamada:

 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
fonte