Jak analizować JSON w Kotlin?

122

Otrzymuję dość głęboki ciąg obiektu JSON z usługi, który muszę przeanalizować do obiektu JSON, a następnie zmapować go na klasy.

Jak mogę przekształcić ciąg JSON w obiekt w Kotlin?

Po tym mapowaniu do odpowiednich klas używałem StdDeserializer firmy Jackson. Problem pojawia się w momencie, gdy obiekt miał właściwości, które również musiały zostać zdeserializowane na klasy. Nie byłem w stanie uzyskać mapowania obiektów, przynajmniej nie wiedziałem jak, wewnątrz innego deserializatora.

Z góry dziękuję za pomoc. Najlepiej, natywnie, staram się zmniejszyć liczbę potrzebnych zależności, więc jeśli odpowiedź dotyczy tylko manipulacji JSON i analizowania, wystarczy.

AJ_1310
źródło
2
Nie rozwijałem się w Javie. To nie jest błąd, który otrzymuję. Po prostu nie wiem, jak natywnie przeprowadzić efektywne parsowanie w Kotlinie. Wszystkie wyszukiwania zawsze prowadzą do struktury. Java ma plik org.json.simple. Ufając funkcjom autouzupełniania IDE, Kotlin nie.
AJ_1310
Pakiet org.json.simple nie jest natywny dla języka Java. Myślę, że to ta biblioteka: github.com/fangyidong/json-simple . Możesz go również użyć z Kotlinem, jeśli chcesz (chociaż biblioteka klaxon, którą zasugerował Jason Bourne, może być lepszym wyborem dla Kotlina).
marstran
Zajrzyj na github.com/square/moshi . Jest o tym post na blogu na medium.com/square-corner-blog/…
James Moore

Odpowiedzi:

73

Możesz użyć tej biblioteki https://github.com/cbeust/klaxon

Klaxon to lekka biblioteka do analizowania JSON w Kotlin.

Istiak Morsalin
źródło
87
Autor tutaj, napisz do mnie, jeśli masz pytania / sugestie.
Cedric Beust
Jakie biblioteki Java muszę zaimportować, aby uzyskać prawidłowy parser? Próbowałem org.json. *, Ale chyba czegoś brakuje w moich ustawieniach Gradle. Myślę, że dokumentacja Klaxon zakłada zbyt wiele (jak użytkownik znający biblioteki Java).
Makis
@CedricBeust Czy próbowałeś pracować z obiektem klasy za pomocą sqlite? jakaś zalecana praktyka tutaj?
Raju yourPepe
105

Nie ma wątpliwości, że przyszłość parsowania w Kotlinie będzie z kotlinx.serialization. Jest częścią bibliotek Kotlin. Jest jeszcze w momencie pisania na etapie inkubatora.

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")
}
Elizeusz Sterngold
źródło
3
Problem, który mam z tym polega na tym, że prawie nie można go używać z lekami generycznymi. Przynajmniej nie wymyśliłem, jak to zrobić. I na pewno nie chcę używać refleksji.
natronite
3
Serializacja KotlinX jest wciąż w fazie eksperymentalnej, więc wprowadzają przełomowe zmiany w nowych wydaniach. Ponadto nie jest bezpieczny dla wątków, więc twój JSON może zostać uszkodzony, jeśli kilka wątków spróbuje użyć jednego wystąpienia Json(jest to powszechne). Ponadto istnieje kilka otwartych problemów z etykietą błędu na Githubie. Powiedziałbym, że nadal jest to ryzykowne w użyciu w produkcji (chyba że chcesz spędzić czas na naprawianiu potencjalnych problemów i nie planujesz często go aktualizować). Projekt jest rzeczywiście interesujący, szczególnie w przypadku projektów Kotlin Multiplatform, ale nie jest jeszcze stabilny.
Javad Sadeqzadeh
Wygląda na to, że nastąpiła już przełomowa zmiana, JSON nazywa się teraz Json
xjcl
Wymaga to również dodatkowej zależności, więc skorzystaj z łącza do przewodnika
xjcl
34

Bez biblioteki zewnętrznej (na Androida)

Aby przeanalizować to:

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

Użyj tych klas:

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

Stosowanie:

val foos = Response(jsonString)
frouo
źródło
2
Więc jeśli to nie wymaga żadnych zewnętrznych bibliotek, powinno to oznaczać, że org.json.JSONObject jest częścią standardowej biblioteki, prawda?
still_dreaming_1
@ still_dreaming_1 tak, to jest, „Dodano na poziomie API 1”, por. developer.android.com/reference/org/json/JSONObject.html
od
Myślę, że to kwestia Androida? Szukałem go w standardowej bibliotece Kotlin lub Java dla JVM.
still_dreaming_1,
O tak, absolutnie, przepraszam, że zapomniałem o tym wspomnieć w odpowiedzi! Może JsonObjectprzydałoby się, jeśli Twoja maszyna JVM działa w środowisku Java7 ( docs.oracle.com/javaee/7/api/javax/json/JsonObject.html )?
od
Och, nie znalazłem tego wcześniej, dzięki! Istnieją również inne pokrewne klasy, takie jak JsonReader. Wygląda na to, że są częścią Java EE, ale nie Java SE. Przyjrzę się ewentualnemu przejściu na Java EE.
still_dreaming_1
26

Możesz użyć Gson.

Przykład

Krok 1

Dodaj kompilację

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

Krok 2

Konwertuj json na Kotlin Bean(użyj JsonToKotlinClass )

Lubię to

Json dane

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

Krok 3

Posługiwać się Gson

var gson = Gson()
var mMineUserEntity = gson?.fromJson(response, MineUserEntity.MineUserInfo::class.java)
KeLiuyue
źródło
to daje mi java.lang.IllegalStateException: Oczekiwano ciągu, ale był BEGIN_OBJECT w linii 1, kolumna 700 ścieżka
Srishti Roy,
Powinieneś najpierw sprawdzić swoje dane zwrotne. @ SrishtiRoy
KeLiuyue
zadziałało, ale jeśli moja odpowiedź json przypomina „kategorie”: [„Zalecane”], to?
Srishti Roy,
@SrishtiRoy odpowiedź to nielegalne dane JSON. Dane prawne JSON zaczynają się od {lub[
KeLiuyue
1
Problem z Gson polega na tym, że ignoruje on wartość zerową. valNieruchomość może być łatwo null, jeśli brakuje z JSON. Ponadto wartości domyślne nie są w ogóle używane.
user3738870
21

Nie jestem pewien, czy tego potrzebujesz, ale tak to zrobiłem.

Korzystanie z importu 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
    }

Oto próbka pliku json:

{"Foods": [{"FoodName": "Jabłka", "Waga": "110"}]}

markB
źródło
8
jaka jest zależność?
Luís Soares
Użyłem org.json. Oto link: mvnrepository.com/artifact/org.json/json/20180813
markB
Ta metoda wymaga, aby klasa miała domyślny konstruktor bez żadnych parametrów. Co jeśli klasa dane mają params w konstruktorze jak poniżej: data class SomeClass(val param1: Int, val param2: Int).
leimenghao
@leimenghao Możesz to zrobić w jednej linii: val Categories = SomeClass (param1 = foodJson.getJSONObject (i) .getString ("FoodName"), param2 = foodJson.getJSONObject (i) .getInt ("Weight"))
markB
Działa naprawdę dobrze. Krótko mówiąc, możesz użyć for (i in 0 until foodJson!!.length()) {zamiast for (i in 0..foodJson!!.length() - 1) {. Robi to samo i jest bardziej wizualny
Arnyminer Z
12

Osobiście używam modułu Jacksona dla Kotlin, który można znaleźć tutaj: jackson-module-kotlin .

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

Jako przykład, oto kod do parsowania JSON drzewa umiejętności Path of Exile, które jest dość ciężkie (84 tys. Wierszy po sformatowaniu):

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

Biorąc pod uwagę twój opis, uważam, że pasuje do twoich potrzeb.

Neurf
źródło
1
W przeciwieństwie do Klaxona (miał błąd, kiedy próbowałem), Jackson faktycznie działa :)
redsk
Ponadto możesz użyć wtyczki klasy danych JSON do Kotlin w intellij, aby wygenerować klasy danych dla siebie.
Brooks DuBois
7

Aby przekonwertować JSON na Kotlin, użyj http://www.json2kotlin.com/

Możesz także użyć wtyczki Android Studio. Plik> Ustawienia, wybierz Pluginsw drzewie po lewej stronie, naciśnij "Przeglądaj repozytoria ...", wyszukaj " JsonToKotlinClass ", zaznacz go i kliknij zielony przycisk "Zainstaluj".

podłącz

Po restarcie AS możesz go używać. Możesz utworzyć klasę za pomocą File > New > JSON To Kotlin Class (JsonToKotlinClass). Innym sposobem jest naciśnięcie Alt + K.

wprowadź opis obrazu tutaj

Następnie zobaczysz okno dialogowe do wklejenia JSON.

W 2018 roku musiałem dodać package com.my.package_namena początku zajęć.

CoolMind
źródło
4

Po pierwsze.

Możesz użyć wtyczki konwertera klas danych JSON na Kotlin w Android Studio do mapowania JSON na klasy POJO (klasa danych kotlin). Ta wtyczka doda adnotacje do klasy danych Kotlin zgodnie z JSON.

Następnie możesz użyć konwertera GSON do konwersji JSON na Kotlin.

Postępuj zgodnie z tym pełnym samouczkiem: Samouczek parsowania Kotlin Android JSON

Jeśli chcesz przeanalizować json ręcznie.

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

Kod do przeanalizowania powyżej tablicy JSON i jej obiektu o indeksie 0.

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

http://www.jsonschema2pojo.org/ Cześć, możesz użyć tej witryny do konwersji json na pojo.
Ctrl + Alt + Shift + K

Następnie możesz ręcznie przekonwertować tę klasę modelu na klasę modelu kotlin. za pomocą powyższego skrótu.

kundan kamal
źródło
1
Konwertuje do Java.
CoolMind
0

Trochę późno, ale nieważne.

Jeśli wolisz parsować JSON od konstrukcji takich jak JavaScript wykorzystujących semantykę Kotlina, polecam JSONKraken , którego jestem autorem.

Sugestie i opinie w tej sprawie są wysoko cenione!

Facundo Garcia
źródło
-4

Pobierz źródło deme stąd ( parsowanie Json w systemie Android Kotlin )

Dodaj tę zależność:

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

Funkcja Call 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
źródło