Wie analysiere ich JSON in Kotlin?

120

Ich erhalte eine ziemlich tiefe JSON-Objektzeichenfolge von einem Dienst, den ich einem JSON-Objekt analysieren und dann Klassen zuordnen muss.

Wie kann ich eine JSON-Zeichenfolge in ein Objekt in Kotlin umwandeln?

Nach der Zuordnung zu den jeweiligen Klassen verwendete ich StdDeserializer von Jackson. Das Problem tritt in dem Moment auf, in dem das Objekt Eigenschaften hatte, die ebenfalls in Klassen deserialisiert werden mussten. Ich konnte den Objekt-Mapper in einem anderen Deserializer nicht bekommen, zumindest wusste ich nicht wie.

Vielen Dank im Voraus für jede Hilfe. Am besten nativ versuche ich, die Anzahl der benötigten Abhängigkeiten zu reduzieren. Wenn die Antwort nur für die JSON-Manipulation und das Parsen lautet, reicht dies aus.

AJ_1310
quelle
2
Ich habe nicht in Java entwickelt. Es ist kein Fehler, den ich bekomme. Ich weiß nur nicht, wie man in Kotlin von Haus aus effektiv analysiert. Alle Suchanfragen führen immer zu einem Framework. Java hat eine org.json.simple. Kotlin vertraut den Autocomplete-Funktionen der IDE nicht.
AJ_1310
Das Paket org.json.simple ist nicht in Java enthalten. Ich denke, es ist diese Bibliothek: github.com/fangyidong/json-simple . Sie können es auch mit Kotlin verwenden, wenn Sie möchten (obwohl die von Jason Bourne vorgeschlagene Klaxon-Bibliothek eine bessere Wahl für Kotlin sein könnte).
Marstran
Schauen Sie sich github.com/square/moshi an . Es gibt einen Blog-Beitrag darüber unter medium.com/square-corner-blog/…
James Moore

Antworten:

71

Sie können diese Bibliothek unter https://github.com/cbeust/klaxon verwenden

Klaxon ist eine leichtgewichtige Bibliothek zum Parsen von JSON in Kotlin.

Istiak Morsalin
quelle
85
Autor hier, zögern Sie nicht, mir eine E-Mail zu senden, wenn Sie Fragen / Vorschläge haben.
Cedric Beust
Welche Java-Bibliotheken muss ich importieren, um den richtigen Parser zu erhalten? Ich habe org.json. * Ausprobiert, aber in meinen Gradle-Einstellungen muss mir etwas fehlen. Ich denke, die Klaxon-Dokumentation setzt zu viel voraus (wie der Benutzer, der die Java-Bibliotheken kennt).
Makis
@CedricBeust haben Sie versucht, mit dem Klassenobjekt mit SQLite zu arbeiten? eine empfohlene Praxis hier?
Raju yourPepe
104

Es steht außer Frage, dass die Zukunft des Parsens in Kotlin bei kotlinx.serialization liegen wird. Es ist Teil der Kotlin-Bibliotheken. Es befindet sich noch zum Zeitpunkt des Schreibens im Inkubatorstadium.

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
quelle
3
Das Problem, das ich damit habe, ist, dass Sie es mit Generika kaum verwenden können. Zumindest habe ich nicht herausgefunden, wie das geht. Und ich möchte auf keinen Fall Reflexion verwenden.
Natronit
3
Die KotlinX-Serialisierung befindet sich noch in der experimentellen Phase, daher werden in neuen Versionen grundlegende Änderungen vorgenommen. Außerdem ist es nicht threadsicher, sodass Ihr JSON möglicherweise beschädigt wird, wenn mehrere Threads versuchen, eine einzelne Instanz von zu verwenden Json(dies ist üblich). Darüber hinaus gibt es mehrere offene Github-Probleme mit dem Bug-Label. Ich würde sagen, es ist immer noch riskant für die Verwendung in der Produktion (es sei denn, Sie sind bereit, Zeit für die Behebung potenzieller Probleme aufzuwenden und planen nicht, es regelmäßig zu aktualisieren). Das Projekt ist zwar besonders für Kotlin Multiplatform-Projekte interessant, aber noch nicht stabil.
Javad Sadeqzadeh
Es scheint bereits eine bahnbrechende Änderung zu geben, JSON heißt jetzt Json
xjcl
Dies erfordert auch eine zusätzliche Abhängigkeit.
Folgen Sie
34

Ohne externe Bibliothek (auf Android)

Um dies zu analysieren:

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

Verwenden Sie diese Klassen:

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

Verwendung:

val foos = Response(jsonString)
frouo
quelle
2
Wenn hierfür keine externen Bibliotheken erforderlich sind, sollte dies bedeuten, dass org.json.JSONObject Teil der Standardbibliothek ist, oder?
still_dreaming_1
@ still_dreaming_1 ja es ist "Hinzugefügt in API Level 1", vgl. developer.android.com/reference/org/json/JSONObject.html
frouo
Ich denke, es ist eine Android-Sache? Ich habe in der Kotlin- oder Java-Standardbibliothek für die JVM danach gesucht.
still_dreaming_1
Oh ja absolut, es tut mir leid, dass ich vergessen habe, das in der Antwort zu erwähnen! Vielleicht könnten Sie verwenden, JsonObjectwenn Ihre JVM unter Java7 ausgeführt wird ( docs.oracle.com/javaee/7/api/javax/json/JsonObject.html )?
Frouo
Oh, das habe ich vorher nicht gefunden, danke! Es gibt auch einige andere verwandte Klassen wie JsonReader. Es scheint, dass sie Teil von Java EE sind, aber nicht von Java SE. Ich werde einen möglichen Wechsel zu Java EE untersuchen.
still_dreaming_1
25

Sie können verwenden Gson.

Beispiel

Schritt 1

Kompilierung hinzufügen

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

Schritt 2

Konvertieren Sie json in Kotlin Bean(verwenden Sie JsonToKotlinClass )

So was

Json Daten

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

Schritt 3

Verwenden Gson

var gson = Gson()
var mMineUserEntity = gson?.fromJson(response, MineUserEntity.MineUserInfo::class.java)
KeLiuyue
quelle
Dies gibt mir java.lang.IllegalStateException: Erwartet eine Zeichenfolge, war aber BEGIN_OBJECT in Zeile 1 Spalte 700 Pfad
Srishti Roy
Sie sollten zuerst Ihre Rückgabedaten überprüfen. @ SrishtiRoy
KeLiuyue
es hat funktioniert, aber wenn meine json-Antwort wie "Kategorien" lautet: ["Empfohlen"], dann?
Srishti Roy
@SrishtiRoy Die Antwort sind illegale JSON-Daten. Legale JSON-Daten werden mit {oder[
KeLiuyue
Das Plugin funktioniert derzeit nicht für verschachtelte JSON-Objekte.
Tom
21

Ich bin mir nicht sicher, ob du das brauchst, aber so habe ich es gemacht.

Verwenden von 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
    }

Hier ist ein Beispiel des json:

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

markB
quelle
8
Was ist die Abhängigkeit?
Luís Soares
Ich habe org.json benutzt. Hier ist der Link: mvnrepository.com/artifact/org.json/json/20180813
markB
Diese Methode erfordert, dass die Klasse einen Standardkonstruktor ohne Parameter hat. Was ist, wenn Datenklassen Parameter wie folgt haben : data class SomeClass(val param1: Int, val param2: Int).
Leimenghao
@leimenghao Sie können dies in einer Zeile tun: val category = SomeClass (param1 = foodJson.getJSONObject (i) .getString ("FoodName"), param2 = foodJson.getJSONObject (i) .getInt ("Weight"))
markB
Funktioniert sehr gut. Nur um zu sagen, Sie können for (i in 0 until foodJson!!.length()) {anstelle von verwenden for (i in 0..foodJson!!.length() - 1) {. Es macht das gleiche und es ist viel visueller
Arnyminer Z
12

Ich persönlich benutze das Jackson-Modul für Kotlin, das Sie hier finden: jackson-module-kotlin .

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

Als Beispiel finden Sie hier den Code zum Parsen des JSON des Skilltree Path of Exile, der ziemlich umfangreich ist (84.000 Zeilen bei Formatierung):

Kotlin-Code:

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 (nicht formatiert): http://filebin.ca/3B3reNQf3KXJ/rawSkilltree.json

Angesichts Ihrer Beschreibung glaube ich, dass es Ihren Bedürfnissen entspricht.

Neurf
quelle
1
Anders als Klaxon (es hatte einen Fehler, als ich es versuchte), funktioniert Jackson tatsächlich :)
Redsk
Darüber hinaus können Sie das JSON-Kotlin-Datenklassen-Plugin in Intellij verwenden, um die Datenklassen für Sie zu generieren.
Brooks DuBois
7

Um JSON in Kotlin zu konvertieren, verwenden Sie http://www.json2kotlin.com/

Sie können auch das Android Studio Plugin verwenden. Datei> Einstellungen, Pluginsim linken Baum auswählen , "Repositorys durchsuchen ..." drücken , " JsonToKotlinClass " suchen , auswählen und auf die grüne Schaltfläche "Installieren" klicken.

Plugin

Nach dem AS-Neustart können Sie es verwenden. Sie können eine Klasse mit erstellen File > New > JSON To Kotlin Class (JsonToKotlinClass). Eine andere Möglichkeit besteht darin, Alt + K zu drücken.

Geben Sie hier die Bildbeschreibung ein

Dann sehen Sie einen Dialog zum Einfügen von JSON.

2018 musste ich zu package com.my.package_nameBeginn einer Klasse hinzufügen .

CoolMind
quelle
4

Zuerst.

Sie können das Plugin zur Konvertierung von JSON in Kotlin-Datenklassen in Android Studio für die JSON-Zuordnung zu POJO-Klassen (Kotlin-Datenklasse) verwenden. Dieses Plugin kommentiert Ihre Kotlin-Datenklasse gemäß JSON.

Dann können Sie den GSON-Konverter verwenden, um JSON in Kotlin zu konvertieren.

Folgen Sie diesem vollständigen Tutorial: Kotlin Android JSON Parsing Tutorial

Wenn Sie json manuell analysieren möchten.

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 zum Parsen über dem JSON-Array und seinem Objekt am Index 0.

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

http://www.jsonschema2pojo.org/ Hallo, Sie können diese Website verwenden, um json in pojo umzuwandeln.
Strg + Alt + Umschalt + k

Danach können Sie diese Modellklasse manuell in eine Kotlin-Modellklasse konvertieren. mit Hilfe der obigen Verknüpfung.

Kundan Kamal
quelle
1
Es wird nach Java konvertiert.
CoolMind
0

Ein bisschen spät, aber was auch immer.

Wenn Sie JSON lieber als JavaScript-ähnliche Konstrukte analysieren möchten , die die Kotlin-Semantik verwenden, empfehle ich JSONKraken , dessen Autor ich bin.

Vorschläge und Meinungen zu diesem Thema werden sehr geschätzt!

Facundo Garcia
quelle
-4

Laden Sie die Quelle von Deme von hier herunter ( Json analysiert in Android Kotlin )

Fügen Sie diese Abhängigkeit hinzu:

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

Rufen Sie die API-Funktion auf:

 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
quelle