วิธีแยกวิเคราะห์ JSON ใน Kotlin


122

ฉันได้รับสตริงออบเจ็กต์ JSON ที่ค่อนข้างลึกจากบริการซึ่งฉันต้องแยกวิเคราะห์ไปยังออบเจ็กต์ JSON จากนั้นแมปกับคลาส

ฉันจะแปลงสตริง JSON เป็นวัตถุใน Kotlin ได้อย่างไร

หลังจากนั้นการแมปไปยังชั้นเรียนที่เกี่ยวข้องฉันใช้ StdDeserializer จาก Jackson ปัญหาเกิดขึ้นในขณะที่อ็อบเจ็กต์มีคุณสมบัติที่ต้องถูก deserialized ลงในคลาสด้วย ฉันไม่สามารถรับตัวทำแผนที่วัตถุได้อย่างน้อยฉันก็ไม่รู้ว่าภายใน deserializer อื่นเป็นอย่างไร

ขอบคุณล่วงหน้าสำหรับความช่วยเหลือ โดยพื้นฐานแล้วฉันกำลังพยายามลดจำนวนการอ้างอิงที่ฉันต้องการดังนั้นหากคำตอบมีไว้สำหรับการจัดการ JSON เท่านั้นและการแยกวิเคราะห์ก็เพียงพอแล้ว


2
ฉันไม่ได้พัฒนาใน Java ไม่ใช่ข้อผิดพลาดที่ฉันได้รับ ฉันไม่รู้วิธีการแยกวิเคราะห์ที่มีประสิทธิภาพใน Kotlin โดยกำเนิด การค้นหาทั้งหมดนำไปสู่กรอบเสมอ Java มี org.json.simple Kotlin ไม่เชื่อในคุณสมบัติการเติมข้อความอัตโนมัติของ IDE
AJ_1310

แพ็คเกจ org.json.simple ไม่ได้มาจาก Java ผมคิดว่ามันเป็นห้องสมุดนี้: github.com/fangyidong/json-simple คุณสามารถใช้กับ Kotlin ได้เช่นกันหากต้องการ (แม้ว่าไลบรารี klaxon ที่ Jason Bourne แนะนำอาจเป็นทางเลือกที่ดีกว่าสำหรับ Kotlin)
marstran

ลองดูที่github.com/square/moshi มีบล็อกโพสต์เกี่ยวกับเรื่องนี้ที่medium.com/square-corner-blog/…
James Moore

คำตอบ:


73

คุณสามารถใช้ไลบรารีนี้ https://github.com/cbeust/klaxon

Klaxon เป็นไลบรารีน้ำหนักเบาสำหรับแยกวิเคราะห์ JSON ใน Kotlin


87
ผู้เขียนที่นี่อย่าลังเลที่จะส่งอีเมลถึงฉันหากคุณมีคำถาม / ข้อเสนอแนะ
Cedric Beust

ฉันต้องนำเข้าไลบรารี java ใดเพื่อให้ได้ Parser ที่ถูกต้อง ฉันลอง org.json. * แต่ฉันต้องพลาดบางอย่างในการตั้งค่า Gradle ของฉัน ฉันคิดว่าเอกสารประกอบของ Klaxon ถือว่ามากเกินไป (เช่นผู้ใช้รู้จักไลบรารี Java)
Makis

@CedricBeust คุณเคยลองทำงานกับคลาส object กับ 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
ปัญหาที่ฉันมีคือคุณแทบจะไม่สามารถใช้กับยาชื่อสามัญได้ อย่างน้อยฉันก็ยังไม่ทราบวิธีการทำ และแน่นอนฉันไม่ต้องการใช้การสะท้อน
natronite

3
KotlinX Serialization ยังอยู่ในขั้นทดลองดังนั้นจึงแนะนำการเปลี่ยนแปลงที่ไม่สมบูรณ์ในรุ่นใหม่ นอกจากนี้ยังไม่ปลอดภัยสำหรับเธรดดังนั้น JSON ของคุณอาจเสียหายหากมีหลายเธรดพยายามใช้อินสแตนซ์เดียวJson(เป็นเรื่องปกติ) นอกจากนี้ยังมีปัญหา Github ที่เปิดอยู่หลายประการเกี่ยวกับป้ายกำกับข้อบกพร่อง ดังนั้นจึงยังคงมีความเสี่ยงที่จะใช้ในการผลิตที่ฉันจะพูด (เว้นแต่คุณยินดีที่จะใช้เวลาในการแก้ไขปัญหาที่อาจเกิดขึ้นและไม่ได้วางแผนที่จะอัปเดตบ่อยๆ) โครงการนี้น่าสนใจโดยเฉพาะอย่างยิ่งสำหรับโครงการ Kotlin Multiplatform แต่ยังไม่เสถียร
Javad Sadeqzadeh

ดูเหมือนว่าจะมีการเปลี่ยนแปลงอย่าง
สิ้นเชิง

นอกจากนี้ยังต้องการการพึ่งพาเพิ่มเติมดังนั้นโปรดไปที่ลิงค์สำหรับคำแนะนำ
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" cf. developer.android.com/reference/org/json/JSONObject.html
ตั้งแต่

ฉันเดาว่ามันเป็นของ 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)

สิ่งนี้ให้ฉัน java.lang.IllegalStateException: คาดว่าจะเป็นสตริง แต่เป็น BEGIN_OBJECT ที่บรรทัด 1 คอลัมน์ 700 พา ธ
Srishti Roy

คุณควรตรวจสอบข้อมูลการคืนสินค้าก่อน @ srishtiRoy
KeLiuyue

มันใช้งานได้ แต่ถ้าการตอบสนอง json ของฉันเป็นแบบ "หมวดหมู่": ["แนะนำ"] งั้นหรือ?
Srishti Roy

@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": "Apples", "Weight": "110"}]}


8
การพึ่งพาคืออะไร?
Luís Soares

ฉันใช้ org.json นี่คือลิงค์: mvnrepository.com/artifact/org.json/json/20180813
markB

วิธีนี้ต้องการให้คลาสต้องมีตัวสร้างเริ่มต้นโดยไม่มีพารามิเตอร์ใด ๆ data class SomeClass(val param1: Int, val param2: Int)เกิดอะไรขึ้นถ้าระดับข้อมูลที่มีพารามิเตอร์ในตัวสร้างที่ต้องการด้านล่าง:
leimenghao

@leimenghao คุณสามารถทำได้ในบรรทัดเดียว: val types = 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) {. มันทำเหมือนกันและมันก็เป็นภาพที่
ชัดเจนกว่า

12

ผมเองใช้โมดูลแจ็กสัน Kotlin ที่คุณสามารถหาที่นี่: jackson โมดูล Kotlin

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

ตัวอย่างเช่นนี่คือโค้ดสำหรับแยกวิเคราะห์ JSON ของสกิล Path of Exile ซึ่งค่อนข้างหนัก (84k บรรทัดเมื่อฟอร์แมต):

รหัส 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 (ไม่ได้จัดรูปแบบ): http://filebin.ca/3B3reNQf3KXJ/rawSkilltree.json

จากคำอธิบายของคุณฉันเชื่อว่าตรงกับความต้องการของคุณ


1
แตกต่างจาก Klaxon (มีข้อผิดพลาดเมื่อฉันพยายาม) แจ็คสันใช้งานได้จริง :)
redsk

นอกจากนี้คุณสามารถใช้ปลั๊กอินคลาสข้อมูล JSON ถึง Kotlin ใน intellij เพื่อสร้างคลาสข้อมูลสำหรับคุณ
Brooks DuBois

7

ในการแปลง JSON เป็น Kotlin ให้ใช้http://www.json2kotlin.com/

นอกจากนี้คุณสามารถใช้ปลั๊กอิน Android Studio ไฟล์> การตั้งค่าเลือกPluginsในโครงสร้างด้านซ้ายกด "เรียกดูที่เก็บ ... " ค้นหา " JsonToKotlinClass " เลือกและคลิกปุ่มสีเขียว "ติดตั้ง"

เสียบเข้าไป

หลังจากรีสตาร์ทคุณสามารถใช้งานได้ คุณสามารถสร้างคลาสด้วยFile > New > JSON To Kotlin Class (JsonToKotlinClass)ไฟล์. อีกวิธีคือกด Alt + K

ใส่คำอธิบายภาพที่นี่

จากนั้นคุณจะเห็นกล่องโต้ตอบเพื่อวาง JSON

ในปี 2018 ฉันต้องเพิ่มpackage com.my.package_nameเมื่อเริ่มชั้นเรียน


4

ก่อนอื่น.

คุณสามารถใช้ปลั๊กอินตัวแปลงคลาส JSON เป็น Kotlin Data ใน Android Studio สำหรับการแมป JSON กับคลาส POJO (คลาสข้อมูล kotlin) ปลั๊กอินนี้จะใส่คำอธิบายประกอบคลาสข้อมูล Kotlin ของคุณตาม JSON

จากนั้นคุณสามารถใช้ตัวแปลง GSON เพื่อแปลง JSON เป็น Kotlin

ทำตามบทช่วยสอนที่สมบูรณ์นี้: บทช่วยสอนการแยกวิเคราะห์ JSON ของ Kotlin Android

หากคุณต้องการแยกวิเคราะห์ 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 Array และวัตถุที่ดัชนี 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 + Shift + K

หลังจากนั้นคุณสามารถแปลงคลาสโมเดลนั้นเป็นคลาสโมเดล kotlin ได้ด้วยตนเอง ด้วยความช่วยเหลือของทางลัดด้านบน


1
มันจะแปลงเป็น Java
CoolMind

0

สายไปหน่อย แต่อะไรก็ได้

หากคุณต้องการแยกวิเคราะห์ JSON เป็น JavaScript เช่นโครงสร้างที่ใช้ความหมายของ Kotlin ฉันขอแนะนำJSONKrakenซึ่งฉันเป็นผู้เขียน

ข้อเสนอแนะและความคิดเห็นเกี่ยวกับเรื่องนี้เป็นที่น่าเสียดายมาก!


-4

ดาวน์โหลดแหล่งที่มาของ deme ได้จากที่นี่ ( Json parsing ใน 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()

        }

    })
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.