Як розібрати JSON у Котліні?


121

Я отримую досить глибокий рядок об'єкта JSON від служби, яку я повинен проаналізувати на об'єкт JSON, а потім зіставити його на класи.

Як я можу перетворити рядок JSON в об'єкт у Котліні?

Після цього для відображення відповідних класів я використовував StdDeserializer від Джексона. Проблема виникає в той момент, коли об’єкт мав властивості, які також довелося десеріалізувати в класи. Мені не вдалося отримати картографію об’єктів, принаймні я не знав як, всередині іншого десеріалізатора.

Заздалегідь дякую за будь-яку допомогу. Переважно, в основному, я намагаюся зменшити кількість залежностей, які мені потрібні, якщо відповідь стосується лише маніпуляцій JSON та їхнього розбору, цього буде достатньо.


2
Я не розвивався в Java. Це не помилка, яку я отримую. Я просто не знаю, як зробити ефективний синтаксичний аналіз в Котліні. Усі пошуки завжди ведуть до рамки. У Java є org.json.simple. Довіряючи функціям автозаповнення IDE, Котлін не робить.
AJ_1310

Пакет org.json.simple не є власником Java. Я думаю, це бібліотека: github.com/fangyidong/json-simple . Ви можете використовувати його і з Котліном, якщо хочете (хоча бібліотека клаксонів, яку запропонував Джейсон Борн, може бути кращим вибором для Котліна).
marstran

Погляньте на github.com/square/moshi . Про це є повідомлення в блозі на сайті medium.com/square-corner-blog/…
Джеймс Мур

Відповіді:


72

Ви можете використовувати цю бібліотеку https://github.com/cbeust/klaxon

Klaxon - це легка бібліотека для розбору JSON у Котліні.


86
Автор тут, не соромтеся надіслати мені електронні листи, якщо у вас є питання / пропозиції
Седрік Беуст

Які бібліотеки Java потрібно імпортувати, щоб отримати правильний Парсер? Я спробував org.json. *, Але мені, мабуть, щось не вистачає в налаштуваннях Gradle Я думаю, що документація Klaxon передбачає занадто багато (як користувач, що знає бібліотеки Java).
Макіс

@CedricBeust Ви намагалися працювати з об’єктом класу з sqlite? будь-яка рекомендована практика тут?
Раджу вашПепе

105

Немає сумніву, що майбутнє розбору в Котліні буде з kotlinx.serialization. Це частина бібліотек Котліна. Це ще під час написання в стадії інкубатора.

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
Проблема, яку я маю з цим, полягає в тому, що ти навряд чи можеш використовувати його з дженериками. Принаймні я не придумав, як це зробити. І я точно не хочу використовувати рефлексію.
натроніт

3
KotlinX Serialization все ще знаходиться в експериментальній фазі, тому вони вносять надзвичайні зміни у нові випуски. Крім того, це не є безпечним для потоків, тому ваш JSON може бути пошкоджений, якщо кілька потоків спробують використовувати один екземпляр Json(це звичайно). Крім того, існує кілька відкритих проблем з Github з міткою про помилку. Отже, це все ще ризиковано використовувати у виробництві (якщо ви не готові витрачати час на виправлення потенційних проблем і не плануєте його часто оновлювати). Проект дійсно цікавий, особливо для проектів Kotlin Multiplatform, але він поки не стабільний.
Джавад Садекзаде

Здається, вже
відбулися

Це також вимагає додаткової залежності, тому перейдіть за посиланням для керівництва
xjcl

34

Без зовнішньої бібліотеки (на Android)

Щоб проаналізувати це:

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

Використовуйте ці класи:

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

Використання:

val foos = Response(jsonString)

2
Отже, якщо для цього не потрібні зовнішні бібліотеки, це означає, що org.json.JSONObject є частиною стандартної бібліотечної права?
still_dreaming_1

@ still_dreaming_1 так, це "Додано в рівні API 1", пор. developer.android.com/reference/org/json/JSONObject.html
frouo

Я думаю, що це Android? Я шукав його в стандартній бібліотеці Котліна або Java для JVM.
still_dreaming_1

О так, я абсолютно вибачте, що забув згадати це у відповіді! Можливо, ви могли б використати, JsonObjectякщо ваш JVM працює під Java7 ( docs.oracle.com/javaee/7/api/javax/json/JsonObject.html )?
frouo

О, я цього раніше не знайшов, дякую! Є також деякі інші пов'язані класи, наприклад, JsonReader. Здається, вони є частиною Java EE, але не Java SE. Я вивчу можливість переходу на Java EE.
still_dreaming_1

26

Можна використовувати Gson.

Приклад

Крок 1

Додати компіляцію

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

Крок 2

Перетворити json в Kotlin Bean(використовувати JsonToKotlinClass )

Подобається це

Json дані

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

Крок 3

Використовуйте Gson

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

це дає мені java.lang.IllegalStateException: Очікуваний рядок, але BEGIN_OBJECT в рядку 1 стовпець 700 шляху
Шрішті Рой

Спершу слід перевірити свої дані про повернення. @ SrishtiRoy
KeLiuyue

він працював, але якщо моя відповідь на json схожа на "категорії": ["Рекомендовано"], тоді?
Шрішті Рой

@SrishtiRoy відповідь є незаконними даними JSON. Юридичні дані JSON починаються з {або[
KeLiuyue

1
Проблема Gson полягає в тому, що він нехтує нульовістю. valВластивість може бути легко , nullякщо він відсутній в форматі JSON. Також значення за замовчуванням взагалі не використовуються.
користувач3738870

21

Не впевнений, що це те, що вам потрібно, але ось як я це зробив.

Використання імпорту 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
    }

Ось зразок json:

{"Foods": [{"FoodName": "Яблука", "Вага": "110"}]}


8
яка залежність?
Луїс Соарес

Я використовував org.json. Ось посилання: mvnrepository.com/artifact/org.json/json/20180813
markB

Цей метод вимагає, щоб у класу був конструктор за замовчуванням без парам. Що робити , якщо клас даних мають Params в конструкторі , як показано нижче: data class SomeClass(val param1: Int, val param2: Int).
leimenghao

@leimenghao Ви можете зробити це в одному рядку: val категорії = SomeClass (param1 = foodJson.getJSONObject (i) .getString ("FoodName"), param2 = foodJson.getJSONObject (i) .getInt ("Вага"))
markB

Працює дуже добре. Просто кажучи, ви можете використовувати for (i in 0 until foodJson!!.length()) {замість for (i in 0..foodJson!!.length() - 1) {. Це робить те саме, і це досить візуально
Arnyminer Z

12

Я особисто використовую модуль Джексона для Котліна, який ви можете знайти тут: jackson-module-kotlin .

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

Як приклад, ось код для розбору JSON майстерні Шляху вигнання, який є досить важким (84 форматів при форматуванні):

Котлін код:

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 (неформатований): http://filebin.ca/3B3reNQf3KXJ/rawSkilltree.json

З огляду на ваш опис, я вважаю, що він відповідає вашим потребам.


1
На відміну від Клаксона (у нього була помилка, коли я спробував), Джексон насправді працює :)
redsk

Крім того, ви можете використовувати плагін класу даних JSON to Kotlin в intellij для створення для вас класів даних.
Брукс Дюбуа

7

Для перетворення JSON в Котлін використовуйте http://www.json2kotlin.com/

Також ви можете використовувати плагін Android Studio. Файл> Налаштування, виберіть Pluginsу лівому дереві, натисніть "Огляд сховищ ...", пошук " JsonToKotlinClass ", виберіть його та натисніть зелену кнопку "Встановити".

підключати

Після перезавантаження AS ви можете використовувати його. Ви можете створити клас за допомогою File > New > JSON To Kotlin Class (JsonToKotlinClass). Інший спосіб - натиснути Alt + K.

введіть тут опис зображення

Тоді ви побачите діалогове вікно, щоб вставити JSON.

У 2018 році мені довелося додати package com.my.package_nameна початку заняття.


4

Поперше.

Ви можете використовувати плагін JSON для Kotlin Data Class Converter в Android Studio для відображення JSON в класи POJO (kotlin data class). Цей плагін буде анотувати ваш клас даних Kotlin згідно JSON.

Тоді ви можете використовувати конвертер GSON для перетворення JSON в Котлін.

Виконайте цей повний підручник: Kotlin Android JSON Parsing Tutorial

Якщо ви хочете розібрати json вручну.

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

Код для розбору над масивом JSON та його об'єктом в індексі 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/ Привіт, ви можете використовувати цей веб-сайт для перетворення json в pojo.
управління + Alt + зсув + k

Після цього ви можете вручну перетворити цей модельний клас у модель класу kotlin. за допомогою наведеного вище ярлика.


1
Він перетвориться на Java.
CoolMind

0

Трохи пізно, але що б там не було.

Якщо ви віддаєте перевагу розбору JSON перед JavaScript, як конструкції, що використовують семантику Котліна , я рекомендую JSONKraken , автором якого я є.

Пропозиції та думки з цього приводу дуже оцінені!


-4

Завантажте джерело deme звідси ( Json розбирає в android kotlin )

Додайте цю залежність:

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

Функція виклику 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()

        }

    })
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.