225

I'm receiving a quite deep JSON object string from a service which I must parse to a JSON object and then map it to classes.

How can I transform a JSON string to object in Kotlin?

After that the mapping to the respective classes, I was using StdDeserializer from Jackson. The problem arises at the moment the object had properties that also had to be deserialized into classes. I was not able to get the object mapper, at least I didn't know how, inside another deserializer.

Preferably, natively, I'm trying to reduce the number of dependencies I need so if the answer is only for JSON manipulation and parsing it'd be enough.

kaya3
  • 47,440
  • 4
  • 68
  • 97
AJ_1310
  • 3,303
  • 3
  • 19
  • 26
  • 3
    I haven't developed in Java. It's not an error I'm getting. I just don't know how to do effective parsing in Kotlin natively. All searches always lead to a framework. Java has an org.json.simple. Trusting the autocomplete features of the IDE, Kotlin doesn't. – AJ_1310 Jan 30 '17 at 04:36
  • The org.json.simple package isn't native to Java. I guess it's this library: https://github.com/fangyidong/json-simple. You could use it with Kotlin as well if you want (although the klaxon library that Jason Bourne suggested might be a better choice for Kotlin). – marstran Jan 30 '17 at 10:03
  • Take a look at https://github.com/square/moshi/. There's a blog post about it at https://medium.com/square-corner-blog/kotlins-a-great-language-for-json-fcd6ef99256b – James Moore Jul 22 '17 at 01:49

17 Answers17

173

There is no question that the future of parsing in Kotlin will be with kotlinx.serialization. It is part of Kotlin libraries. Version kotlinx.serialization 1.0 is finally released

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
  • 2,324
  • 1
  • 16
  • 14
  • 6
    The problem I have with this is that you can hardly use it with generics. At least I have not figured out how to do so. And I certainly don't want to use reflection. – natronite Dec 13 '19 at 06:33
  • 7
    KotlinX Serialization is still in experimental phase, so they introduce breaking changes in new releases. Also, it's not thread-safe, so your JSON might get corrupted if several threads try to use a single instance of `Json` (it's common). Furthermore, there are several open Github issues with the bug label. So, it's still risky for use in production I would say (unless you're willing to spend time on fixing potential issues and do not plan to update it frequently). The project is indeed interesting especially for Kotlin Multiplatform projects, but it's not stable yet. – Javad May 03 '20 at 22:06
  • 1
    There seems to already be a breaking change, JSON is now called Json – xjcl May 22 '20 at 22:27
  • This also requires an additional dependency, so do follow the link for a guide – xjcl May 22 '20 at 22:27
  • 2020 Sept and this is still at RC2, no 1.0 version yet :-( – TimCO Oct 02 '20 at 01:33
  • 6
    Finally 1.0 is out! Here same examples: Https://blog.jetbrains.com/kotlin/2020/10/kotlinx-serialization-1-0-released/ – fisio Oct 12 '20 at 10:59
  • See the kotlinx.serialization enhanced documentation on GitHub: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/basic-serialization.md – Arash Kamangir Jan 21 '21 at 19:23
  • 1
    https://kotlinlang.org/docs/releases.html shows this plugin many times. It seems to be the best way to work with json in Kotlin. – Caio Santos Apr 19 '21 at 20:02
  • 1
    Updated answer : https://stackoverflow.com/a/63820753/3333438 – Banee Ishaque K Jul 22 '22 at 00:53
  • can I just use `val b: String?` instead of `@Optional val b: String` as witth `Gson`? – user924 Mar 20 '23 at 16:15
  • it's `val parsedError: NetworkError = Json.decodeFromString(errorJsonBody)` now – user924 Mar 20 '23 at 16:18
105

You can use this library https://github.com/cbeust/klaxon

Klaxon is a lightweight library to parse JSON in Kotlin.

Istiak Morsalin
  • 10,621
  • 9
  • 33
  • 65
  • 161
    Author here, feel free to email me if you have questions/suggestions. – Cedric Beust Jan 30 '17 at 17:59
  • I tried this today after two days of stuffing around with other libraries, and it works really well. Polymorphic support and documentation look good too, after collections it's the second thing I always test. Great package @Cedric! – Mark Feldman Sep 24 '20 at 01:07
  • 1
    why use a library when built-in support is available? – AppDeveloper Dec 12 '20 at 12:20
  • if you are looking at doing multi-platform or want the best performance. you should use Kotlinx serialization below. – sonique Jun 27 '21 at 13:05
  • 2
    Why do we need a third party library to parse JSON? –  Apr 28 '22 at 10:58
54

You can use Gson .

Example

Step 1

Add compile

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

Step 2

Convert json to Kotlin Bean(use JsonToKotlinClass)

Like this

Json data

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

Step 3

Use Gson

var gson = Gson()
var mMineUserEntity = gson?.fromJson(response, MineUserEntity.MineUserInfo::class.java)
KeLiuyue
  • 8,149
  • 4
  • 25
  • 42
48

Without external library (on Android)

To parse this:

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

Use these classes:

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

Usage:

val foos = Response(jsonString)
Community
  • 1
  • 1
frouo
  • 5,087
  • 3
  • 26
  • 29
  • 4
    So if this does not require any external libraries, that should mean org.json.JSONObject is part of the standard library right? – still_dreaming_1 Jul 14 '19 at 16:59
  • 2
    @still_dreaming_1 yes it is, "Added in API level 1", cf. https://developer.android.com/reference/org/json/JSONObject.html – frouo Jul 16 '19 at 07:48
  • 3
    I guess it's an Android thing? I was looking for it in the Kotlin or Java standard library for the JVM. – still_dreaming_1 Jul 16 '19 at 12:54
  • Oh yes absolutely, I am sorry I forgot to mention that in the answer! Maybe you could use `JsonObject` if your JVM is running under Java7 (https://docs.oracle.com/javaee/7/api/javax/json/JsonObject.html)? – frouo Jul 16 '19 at 15:27
  • It must have a performance problem since you re-parsed JSONObject for every key. – Cao Dongping Dec 31 '19 at 06:38
  • What if I have sth nested `{profile: { name: "hi" friends: [{ name: "henlo" }], score: { final: 100 } }}` – Newbie... Sep 22 '22 at 12:43
37

Not sure if this is what you need but this is how I did it.

Using 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
    }

Here's a sample of the json :

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

markB
  • 397
  • 2
  • 5
  • 12
    what's the dependency? – Luís Soares Jul 22 '17 at 10:08
  • 2
    I used org.json. Here's the link : https://mvnrepository.com/artifact/org.json/json/20180813 – markB Feb 21 '20 at 11:04
  • This method requires that class must have default constructor without any params. What if data class have params in constructor like below: ```data class SomeClass(val param1: Int, val param2: Int)```. – leimenghao Jul 01 '20 at 09:44
  • @leimenghao You can do this in one line : val categories = SomeClass(param1 = foodJson.getJSONObject(i).getString("FoodName"),param2 = foodJson.getJSONObject(i).getInt("Weight")) – markB Jul 02 '20 at 12:51
  • Works really well. Just to say, you can use `for (i in 0 until foodJson!!.length()) {` instead of `for (i in 0..foodJson!!.length() - 1) {`. It does the same, and it's quite more visual – Arnyminer Z Jul 20 '20 at 16:57
15

I personally use the Jackson module for Kotlin that you can find here: jackson-module-kotlin.

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

As an example, here is the code to parse the JSON of the Path of Exile skilltree which is quite heavy (84k lines when formatted) :

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

Given your description, I believe it matches your needs.

hoijui
  • 3,615
  • 2
  • 33
  • 41
Neurf
  • 151
  • 2
12

This uses kotlinx.serialization like Elisha's answer. Meanwhile the project is past version 1.0 so the API has changed. Note that e.g. JSON.parse was renamed to Json.decodeFromString. Also it is imported in gradle differently starting in Kotlin 1.4.0:

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0"
}
apply plugin: 'kotlinx-serialization'

Example usage:

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
@Serializable
data class Point(val x: Int, val y: Int)

val pt = Json.decodeFromString<Point>("""{"y": 1, "x": 2}""")
val str = Json.encodeToString(pt)  // type can be inferred!

val ilist = Json.decodeFromString<List<Int>>("[-1, -2]")
val ptlist = Json.decodeFromString<List<Point>>(
    """[{"x": 3, "y": 4}, {"x": 5, "y": 6}]"""
)

You can use nullable types (T?) for both nullable and optional fields:

@Serializable
data class Point2(val x: Int, val y: Int? = null)

val nlist = Json.decodeFromString<List<Point2>>(
    """[{"x": 7}, {"x": 8, "y": null}, {"x": 9, "y": 0}]"""
)

Kotlin's data class is a class that mainly holds data and has members, .toString() and other methods (e.g. destructuring declarations) automatically defined.

xjcl
  • 12,848
  • 6
  • 67
  • 89
  • `val pt = Json.decodeFromString("""{"y": 1, "x": 2}""")` - `Type mismatch. Required: DeserializationStrategy Found: String`, it takes two parameters – user924 Mar 20 '23 at 16:14
  • I will check if my answer needs updating. What version did you use? – xjcl Mar 20 '23 at 23:37
9

GSON is a good choice for Android and Web platform to parse JSON in a Kotlin project. This library is developed by Google. https://github.com/google/gson

1. First, add GSON to your project:

dependencies {
    implementation 'com.google.code.gson:gson:2.8.9'
}

2. Now you need to convert your JSON to Kotlin Data class:

Copy your JSON and go to this(https://json2kt.com) website and paste your JSON to Input Json box. Write package(ex: com.example.appName) and Class name(ex: UserData) in proper box. This site will show live preview of your data class below and also you can download all classes at once in a zip file.

After downloading all classes extract the zip file & place them into your project.

3. Now Parse like below:

val myJson = """
{
    "user_name": "john123",
    "email": "john@example.com",
    "name": "John Doe"
}
""".trimIndent()

val gson = Gson()
var mUser = gson.fromJson(myJson, UserData::class.java)
println(mUser.userName)

Done :)

Positive Navid
  • 2,481
  • 2
  • 27
  • 41
Touhid
  • 1,556
  • 18
  • 18
7

First of all.

You can use JSON to Kotlin Data class converter plugin in Android Studio for JSON mapping to POJO classes (kotlin data class). This plugin will annotate your Kotlin data class according to JSON.

Then you can use GSON converter to convert JSON to Kotlin.

Follow this Complete tutorial: Kotlin Android JSON Parsing Tutorial

If you want to parse json manually.

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 to Parse above JSON Array and its object at index 0.

var jsonArray = JSONArray(sampleJson)
for (jsonIndex in 0..(jsonArray.length() - 1)) {
Log.d("JSON", jsonArray.getJSONObject(jsonIndex).getString("title"))
}
Developine
  • 12,483
  • 8
  • 38
  • 42
7

To convert JSON to Kotlin use http://www.json2kotlin.com/

Also you can use Android Studio plugin. File > Settings, select Plugins in left tree, press "Browse repositories...", search "JsonToKotlinClass", select it and click green button "Install".

plugin

After AS restart you can use it. You can create a class with File > New > JSON To Kotlin Class (JsonToKotlinClass). Another way is to press Alt + K.

enter image description here

Then you will see a dialog to paste JSON.

In 2018 I had to add package com.my.package_name at the beginning of a class.

CoolMind
  • 26,736
  • 15
  • 188
  • 224
5

Kotlin Serialization

Kotlin specific library by JetBrains for all supported platforms – Android, JVM, JavaScript, Native.

https://github.com/Kotlin/kotlinx.serialization

Moshi

Moshi is a JSON library for Android and Java by Square.

https://github.com/square/moshi

Jackson

https://github.com/FasterXML/jackson

Gson

Most popular but almost deprecated.

https://github.com/google/gson

JSON to Java

http://www.jsonschema2pojo.org/

JSON to Kotlin

IntelliJ plugin - https://plugins.jetbrains.com/plugin/9960-json-to-kotlin-class-jsontokotlinclass-

Freek de Bruijn
  • 3,552
  • 2
  • 22
  • 28
iamanbansal
  • 2,412
  • 2
  • 12
  • 20
3

Parse JSON string to Kotlin object

As others recommend, Gson library is the simplest way!

If the File is in the Asset folder you can do like this, first add

dependencies {
  implementation 'com.google.code.gson:gson:2.9.0'
}

then get a file from Asset:

jsonString = context.assets.open(fileName).bufferedReader().use { it.readText() }

then use Gson :

val gson = Gson()
val listPersonType = object : TypeToken<List<Person>>() {}.type
var persons: List<Person> = gson.fromJson(jsonFileString, listPersonType)
persons.forEachIndexed { idx, person -> Log.i("data", "> Item $idx:\n$person") }

Where Person is a Model/Data class, like this

data class Person(val name: String, val age: Int, val messages: List) {
}
Mori
  • 2,653
  • 18
  • 24
3

I created a simple Extention function to convert JSON string to model class

inline fun <reified T: Any> String.toKotlinObject(): T =
    Gson().fromJson(this, T::class.java)

Usage method

stringJson.toKotlinObject<MyModelClass>()
Hamdy Abd El Fattah
  • 1,405
  • 1
  • 16
  • 16
2

If you prefer parsing JSON to JavaScript-like constructs making use of Kotlin syntax, I recommend JSONKraken, of which I am the author.

You can do things like:

val json: JsonValue = JsonKraken.deserialize("""{"getting":{"started":"Hello World"}}""")
println(JsonKraken.serialize(json)) //prints: {"getting":{"started":"Hello World"}}
println(json["getting"]["started"].cast<String>()) //prints: Hello World

Suggestions and opinions on the matter are much apreciated!

1

http://www.jsonschema2pojo.org/ Hi you can use this website to convert json to pojo.
control+Alt+shift+k

After that you can manualy convert that model class to kotlin model class. with the help of above shortcut.

kundan kamal
  • 674
  • 7
  • 16
0

Seems like Kotlin does not have any built-in method as in many cases it just imports and implements some tools from Java. After trying lots of packages, finally this one worked reasonably. This fastjson from alibaba, which is very easy to use. Inside build gradle dependencies:

implementation 'com.alibaba:fastjson:1.1.67.android'

Inside your Kotlin code:

import com.alibaba.fastjson.JSON
var jsonDecodedMap: Map<String, String> =
            JSON.parse(yourStringValueHere) as Map<String, String>;
Elmar
  • 2,235
  • 24
  • 22
-6

Download the source of deme from here(Json parsing in android kotlin)

Add this dependency:

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

Call api function:

 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()

        }

    })
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Deepshikha Puri
  • 2,104
  • 22
  • 23