Как разобрать JSON в Котлине?

122

Я получаю довольно глубокую строку объекта JSON от службы, которую я должен проанализировать на объект JSON, а затем сопоставить ее с классами.

Как я могу преобразовать строку JSON в объект в Котлине?

После этого сопоставления с соответствующими классами я использовал StdDeserializer от Jackson. Проблема возникает в тот момент, когда у объекта есть свойства, которые также необходимо десериализовать в классы. Мне не удалось получить сопоставитель объектов внутри другого десериализатора, по крайней мере, я не знал, как это сделать.

Заранее благодарю за любую помощь. Желательно, изначально, я пытаюсь уменьшить количество необходимых мне зависимостей, поэтому, если ответ касается только манипуляций и синтаксического анализа JSON, этого будет достаточно.

AJ_1310
источник
2
Я не занимался разработкой на Java. Это не ошибка. Я просто не знаю, как сделать эффективный синтаксический анализ в Котлине изначально. Все поиски всегда приводят к рамкам. В Java есть файл org.json.simple. Kotlin не доверяет функциям автозаполнения в IDE.
AJ_1310
Пакет org.json.simple не является родным для Java. Думаю, это библиотека: github.com/fangyidong/json-simple . Вы можете использовать его и с Kotlin, если хотите (хотя библиотека klaxon, которую предложил Джейсон Борн, может быть лучшим выбором для Kotlin).
marstran
Взгляните на github.com/square/moshi . Об этом есть запись в блоге на medium.com/square-corner-blog/…
Джеймс Мур,

Ответы:

73

Вы можете использовать эту библиотеку https://github.com/cbeust/klaxon

Klaxon - это легкая библиотека для анализа JSON в Kotlin.

Истяк Морсалин
источник
87
Автор здесь, напишите мне, если у вас есть вопросы / предложения.
Cedric Beust
Какие библиотеки java мне нужно импортировать, чтобы получить правильный парсер? Я пробовал org.json. *, Но мне что-то не хватает в настройках Gradle. Я думаю, что документация Klaxon предполагает слишком многое (например, пользователь, знакомый с библиотеками Java).
Makis
@CedricBeust пробовали ли вы работать с объектом класса с sqlite? какая-нибудь рекомендуемая практика здесь?
Raju yourPepe
105

Нет никаких сомнений в том, что будущее парсинга в Kotlin будет за kotlinx.serialization. Это часть библиотек Kotlin. На момент написания он все еще находится на стадии инкубатора.

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, но пока он нестабилен.
Джавад Садекзаде
Кажется, уже есть критическое изменение, JSON теперь называется Json
xjcl
Это также требует дополнительной зависимости, поэтому перейдите по ссылке для руководства
xjcl
35

Без внешней библиотеки (на 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)
frouo
источник
2
Итак, если для этого не нужны внешние библиотеки, это должно означать, что org.json.JSONObject является частью стандартной библиотеки, верно?
still_dreaming_1
1
@ still_dreaming_1 да, «Добавлено в API уровня 1», ср. developer.android.com/reference/org/json/JSONObject.html
frouo
Думаю, это Android? Я искал его в стандартной библиотеке Kotlin или 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)
KeLiuyue
источник
это дает мне java.lang.IllegalStateException: ожидалась строка, но она была BEGIN_OBJECT в строке 1 столбца 700 путь
Сришти Рой
Вы должны сначала проверить данные возврата. @ SrishtiRoy
KeLiuyue
это сработало, но если мой ответ json похож на "категории": ["Рекомендуется"], тогда?
Сришти Рой
@SrishtiRoy ответ - недопустимые данные JSON. Правовые данные JSON начинаются с {или[
KeLiuyue
1
Проблема с Gson заключается в том, что он игнорирует возможность нулевого значения. valСвойство может быть легко , nullесли он отсутствует в формате JSON. Кроме того, значения по умолчанию вообще не используются.
user3738870
21

Не уверен, что это то, что вам нужно, но я сделал это так.

Используя 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
    }

Вот образец json:

{"Еда": [{"FoodName": "Яблоки", "Вес": "110"}]}

markB
источник
8
какая зависимость?
Луис Соарес
Я использовал org.json. Вот ссылка: mvnrepository.com/artifact/org.json/json/20180813
markB
Этот метод требует, чтобы класс имел конструктор по умолчанию без каких-либо параметров. Что делать , если класс данных имеют Params в конструкторе , как показано ниже: data class SomeClass(val param1: Int, val param2: Int).
leimenghao
@leimenghao Вы можете сделать это одной строкой: val category = SomeClass (param1 = foodJson.getJSONObject (i) .getString ("FoodName"), param2 = foodJson.getJSONObject (i) .getInt ("Weight"))
markB
Очень хорошо работает. Просто чтобы сказать, вы можете использовать for (i in 0 until foodJson!!.length()) {вместо for (i in 0..foodJson!!.length() - 1) {. Он делает то же самое, но более наглядно
Arnyminer Z
12

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

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

В качестве примера, вот код для синтаксического анализа JSON дерева навыков Path of Exile, который довольно тяжелый (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

Учитывая ваше описание, я считаю, что оно соответствует вашим потребностям.

Neurf
источник
1
В отличие от Klaxon (у него была ошибка, когда я пытался), Jackson действительно работает :)
redsk
Кроме того, вы можете использовать плагин класса данных JSON to Kotlin в intellij для создания классов данных для вас.
Brooks DuBois
7

Чтобы преобразовать JSON в Kotlin, используйте 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в начале урока.

CoolMind
источник
4

Прежде всего.

Вы можете использовать плагин преобразования классов JSON в Kotlin Data в Android Studio для сопоставления JSON с классами POJO (класс данных kotlin). Этот плагин аннотирует ваш класс данных Kotlin в соответствии с JSON.

Затем вы можете использовать конвертер GSON для преобразования JSON в Kotlin.

Следуйте этому полному руководству: Руководство по синтаксическому анализу Kotlin Android JSON

Если вы хотите разобрать 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"))
}
Developine
источник
1

http://www.jsonschema2pojo.org/ Привет, вы можете использовать этот веб-сайт для преобразования json в pojo.
Ctrl + Alt + Shift + K

После этого вы можете вручную преобразовать этот класс модели в класс модели kotlin. с помощью вышеуказанного ярлыка.

Кундан Камал
источник
1
Он будет преобразован в Java.
CoolMind
0

Немного поздно, но все равно.

Если вы предпочитаете анализировать JSON, а не JavaScript, например конструкции, использующие семантику Kotlin, я рекомендую JSONKraken , автором которого я являюсь.

Предложения и мнения по этому поводу приветствуются!

Факундо Гарсия
источник
-4

Загрузите исходный код дема отсюда ( парсинг 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()

        }

    })
Глубокий Пури
источник