Parsing JsonObject into kotlin Data class - android

I am fairly new to Kotlin and I am trying to parse the JsonObject that I get from my HTTPRequest into data class. But I cannot get the Instance from the data class.
val jsonObjectRequest = JsonObjectRequest(Request.Method.GET, url, null,
Response.Listener { response ->
val text = "Response: %s".format(response.toString())
print(text)
},
Response.ErrorListener { error ->
// TODO: Handle error
}
)
#Throws(AuthFailureError::class)
#Override
fun getHeaders(): Map<String, String> {
var params = HashMap<String, String>()
if (params == null) params = HashMap()
val basicAuth = "Basic " + Base64.encodeToString("username:password".toByteArray(), Base64.NO_WRAP);
params["Authorization"] = basicAuth
//..add other headers
return params
}
TempJsonObject.instance?.addToRequestQueue(jsonObjectRequest)
Data Class
data class TempJsonObject(
val content: List<Content>) {
data class Content(
val kioskId: Int,
val location: Location,
val stats: Stats,
val settings: Settings,
val modules: String,
val visibility: String
) {
data class Stats(
val allLockers: Int,
val emptyLockers: Int,
val malfunctionLockers: Int,
val forCustomer: Int,
val forCourier: Int,
val bySize: BySize,
val incoming: Any
) {
data class BySize(
val XXSMALL: Xxsmall,
val XSMALL: Xsmall,
val SMALL: Small,
val MEDIUM: Medium,
val LARGE: Large,
val XLARGE: Xlarge
) {
data class Xxsmall(
val empty: Int,
val hasContent: Int
)
...
Is the way I am trying to do it the right approach and what am I doing wrong?

Looking at instance?.addToRequestQueue, it's really puzzling me. I don't know any Kotlin API like this for parsing JSONs. I thought that you use some library that adds such API, but you also wrote in the comment that the code doesn't compile because of lack of instance field. It looks like it exposes some singleton instance of the data class. Could you point me to any working example using such API, or any other source that you used?
If the above turns out to be incorrect, I would just use some Kotlin or Java library to parse the JSON into an instance of the data class. One option is the Kotlin library Klaxon: https://github.com/cbeust/klaxon. Quoting one of its examples that should fit your use case:
val result = Klaxon().parse<Person>(""" { "name": "John Smith", } """)
assert(result?.name == "John Smith")
assert(result.age == 23)

Related

Parsing Nested Polymorphic Objects with GSON and Retrofit

I am trying to show list of messages with different types of ViewHolders i.e. Text, ImageText, Video etc. I get a list of these objects from API somewhat in this format:
{
"message":"success",
"total_pages":273,
"current_page":1,
"page_size":10,
"notifications":[
{
"id":4214,
"notification_message":"test notification 1",
"meta_data":{
"messageId":"19819189",
"viewHolderType":"textOnly",
"body":{
"time":"10-06-21T02:31:29,573",
"type":"notification",
"title":"Hi, Welcome to the NT experience",
"description":"This is the welcome message",
"read":true
}
}
},
{
"id":9811,
"notification_message":"test vss notification",
"meta_data":{
"messageId":"2657652",
"viewHolderType":"textWithImage",
"body":{
"time":"11-06-21T02:31:29,573",
"type":"promotions",
"title":"Your Package - Premium",
"description":"Thank you for subscribing to the package. Your subscription entitles you to Premium 365 Days Plan (worth $76.61)",
"headerImage":"www.someurl.com/image.jpg",
"read":true
}
}
}
]
}
Now I have to parse this list from network module for client module which will use only the objects inside meta_data. To that end I have created following classes:
open class BaseMessageListItem
internal data class MessageListResponse(
#field:SerializedName("current_page")
val current_page: Int,
#field:SerializedName("notifications")
val notifications: List<MessageListItem>,
#field:SerializedName("message")
val message: String,
#field:SerializedName("page_size")
val page_size: Int,
#field:SerializedName("total_page")
val total_page: Int
)
internal data class MessageListItem(
#field:SerializedName(“id”)
val id: String,
#field:SerializedName("notification_message")
val notification_message: String,
#field:SerializedName("meta_data")
val meta_data: MessageListMetaDataItem,
)
internal data class MessageListMetaDataItem(
#field:SerializedName("messageId")
val messageId: String = "",
#field:SerializedName("viewHolderType")
val viewHolderType: String = "",
#field:SerializedName("body")
val body: BaseMessageListItem = BaseMessageListItem()
)
internal data class ImageMessageListItem(
#field:SerializedName("description")
val description: String,
#field:SerializedName("headerImage")
val headerImage: String,
#field:SerializedName("read")
val read: Boolean,
#field:SerializedName("time")
val time: String,
#field:SerializedName("title")
val title: String,
#field:SerializedName("type")
val type: String
): BaseMessageListItem()
internal data class TextMessageListItem(
#field:SerializedName("description")
val description: String,
#field:SerializedName("read")
val read: Boolean,
#field:SerializedName("time")
val time: String,
#field:SerializedName("title")
val title: String,
#field:SerializedName("type")
val type: String
): BaseMessageListItem()
The notifications>meta_data>body can be polymorphic. I have set of classes (for ImageItem, ImageWithTextItem, VideoItem etc) which extend to BaseMessageListItem.
private var runtimeTypeAdapterFactory: RuntimeTypeAdapterFactory<BaseMessageListItem> = RuntimeTypeAdapterFactory
.of(BaseMessageListItem::class.java, "viewHolderType")
.registerSubtype(ImageMessageListItem::class.java, MessageListItemTypes.TEXT_WITH_IMAGE.value)
.registerSubtype(TextMessageListItem::class.java, MessageListItemTypes.TEXT_ONLY.value)
private var gson: Gson = GsonBuilder()
.registerTypeAdapterFactory(runtimeTypeAdapterFactory)
.create()
I tried parsing it using viewHolderType in RuntimeTypeAdapterFactory but since it's not a property of BaseMessageListItem, it is not able to parse it.
Any one has any experience dealing with this type of JSON, please do share any pointers.
RuntimeTypeAdapterFactory requires the viewHolderType field to be put right into the body objects. In order to fix this, you have
either patch RuntimeTypeAdapterFactory (it is not even published as a compiled JAR, but rather still retains in the public repository as source code free to modify), or fix your class hierarchy to lift up the missing field because it can only work with fields on the same nest level.
internal var gson: Gson = GsonBuilder()
.registerTypeAdapterFactory(
RuntimeTypeAdapterFactory.of(BaseMessageListMetaDataItem::class.java, "viewHolderType")
.registerSubtype(TextWithImageMessageListMetaDataItem::class.java, "textWithImage")
.registerSubtype(TextOnlyMessageListMetaDataItem::class.java, "textOnly")
)
.create()
internal data class MessageListItem(
#field:SerializedName("meta_data")
val metaData: BaseMessageListMetaDataItem<*>?,
)
internal abstract class BaseMessageListMetaDataItem<out T>(
#field:SerializedName("viewHolderType")
val viewHolderType: String?,
#field:SerializedName("body")
val body: T?
) where T : BaseMessageListMetaDataItem.Body {
internal abstract class Body
}
internal class TextOnlyMessageListMetaDataItem
: BaseMessageListMetaDataItem<TextOnlyMessageListMetaDataItem.Body>(null, null) {
internal data class Body(
#field:SerializedName("title")
val title: String?
) : BaseMessageListMetaDataItem.Body()
}
internal class TextWithImageMessageListMetaDataItem
: BaseMessageListMetaDataItem<TextWithImageMessageListMetaDataItem.Body>(null, null) {
internal data class Body(
#field:SerializedName("title")
val title: String?,
#field:SerializedName("headerImage")
val headerImage: String?
) : BaseMessageListMetaDataItem.Body()
}
I might be understanding you wrong, but I would like to suggest a different approach. I am assuming you would like to assign to get a ViewHolder type directly from what you get in your API response.
There are two approaches I would like to suggest:
First, if it is possible to get the API response modified, I would suggest to change viewHolderType from a String to an Int so as you can be clear with your mapping and then you can directly compare it.
Second what I would suggest is to keep another key in your data class which sets value as per the viewHolderType it receives which would be something of as follows.
internal data class MessageListMetaDataItem(
#field:SerializedName("messageId")
val messageId: String = "",
#field:SerializedName("viewHolderType")
val viewHolderType: String = "",
#field:SerializedName("body")
val body: BaseMessageListItem = BaseMessageListItem()
) {
val viewHolderMapping: Int
get() = when(viewHolderType){
"textOnly" -> MessageListItemTypes.TEXT_ONLY
"textWithImage" -> MessageListItemTypes.TEXT_WITH_IMAGE
else -> MessageListItemTypes.UNKNOWN_TYPE
}
}

How To Filter Retrofit2 Json Response To Keep Only Certain Elements In ArrayList

I'm new to kotlin so this maybe a very easy issue to resolve.
What I'm trying to do is filter the json response that I receive using Retrofit2 before I display the images in a grid with a RecyclerView.
instagram.com/explore/tags/{hashtag}/?__a=1&max_id= Using Retrofit2 I'm able to get the data response fine and also display the given url images in a RecyclerView.
I have not been successful in using the filter, map, loops and conditions to remove elements from the Arraylist. I do not understand these to the fullest extent but I have searched looking for solutions and those are what I came apon.
Interface
interface InstagramDataFetcher
{
#GET("tags/{tag}/?__a=1&max_id=")
fun getInstagramData(#Path("tag") hashtag: String) : Call <InstagramResponse>
}
Where I get my response from and also get StringIndexOutOfBoundsException
class InstagramFeedFragment : Fragment()
{
private fun onResponse()
{
val service = RestAPI.retrofitInstance?.create(InstagramDataFetcher::class.java)
val call = service?.getInstagramData("hashtag")
call?.enqueue(object : Callback<InstagramResponse>
{
override fun onFailure(call: Call<InstagramResponse>, t: Throwable)
{
Log.d("FEED", " $t")
}
override fun onResponse(
call: Call<InstagramResponse>, response: Response<InstagramResponse>
)
{
//for ((index, value) in data.withIndex())
if (response.isSuccessful)
{
var data: ArrayList<InstagramResponse.InstagramEdgesResponse>? = null
val body = response.body()
data = body!!.graphql.hashtag.edge_hashtag_to_media.edges
for ((index, value) in data.withIndex())
{
if(value.node.accessibility_caption[index].toString().contains("text") ||
value.node.accessibility_caption[index].toString().contains("person"))
{
data.drop(index)
}
}
recyclerView.adapter = InstagramGridAdapter(data, parentFragment!!.context!!)
}
}
})
}
}
This is my model class
data class InstagramResponse(val graphql: InstagramGraphqlResponse)
{
data class InstagramGraphqlResponse(val hashtag: InstagramHashtagResponse)
data class InstagramHashtagResponse(val edge_hashtag_to_media: InstagramHashtagToMediaResponse)
data class InstagramHashtagToMediaResponse(
val page_info: InstagramPageInfo,
val edges: ArrayList<InstagramEdgesResponse>
)
data class InstagramPageInfo(
val has_next_page: Boolean,
val end_cursor: String
)
data class InstagramEdgesResponse(val node: InstagramNodeResponse)
data class InstagramNodeResponse(
val __typename: String,
val shortcode: String,
val display_url: String,
val thumbnail_src: String,
val thumbnail_resources: ArrayList<InstagramThumbnailResourceResponse>,
val is_video: Boolean,
val accessibility_caption: String
)
data class InstagramThumbnailResourceResponse(
val src: String,
val config_width: Int,
val config_height: Int
)
}
Simply again, I want to just remove elements from the arraylist that match certain things what I don't want. For instance. the "is_video" value that comes from the json. I want to go through the arraylist and remove all elements that have "is_video" as true.
Thanks
If you asking how to filter the list then below is the demo.
You just need to use filter on your data which is an ArrayList. I've tried keeping the same structure for the models so that you can get a better understanding.
fun main() {
val first = InstagramNodeResponse(
title = "first",
is_video = true
)
val second = InstagramNodeResponse(
title = "second",
is_video = false
)
val list: ArrayList<InstagramEdgesResponse> = arrayListOf(
InstagramEdgesResponse(node = first),
InstagramEdgesResponse(node = second)
)
val itemsWithVideo = list.filter { it.node.is_video == true }
val itemsWithoutVideo = list.filter { it.node.is_video == false }
println(itemsWithVideo.map { it.node.title }) // [first]
println(itemsWithoutVideo.map { it.node.title }) // [second]
}
// Models
data class InstagramEdgesResponse(val node: InstagramNodeResponse)
data class InstagramNodeResponse(
val title: String,
val is_video: Boolean
)

Android Kotlin - Sum of JSON objects

I have two JSON files which are connected using foreign key/field (in my case one is Post( id, ...) and Comments ( postId, ...)). I need to display total number of comments per post (in my case it's always 5).
My data classes are as follows:
data class Posts(val userId: Int,
val id: Int,
val title: String,
val body: String)
data class Comments(val postId: Int,
val id: Int,
val name: String,
val email: String,
val body: String)
And here is the function which I use to get json data (I use okhttp for setting up the client and gson for getting the data):
private fun fetchCommentJson() {
val postId = intent.getIntExtra(POST_ID, -1)
val commentJsonData = commentsJSON
val client = OkHttpClient()
val request = Request.Builder().url(commentJsonData).build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call?, e: IOException?) {
toast("Something went wrong fetching your data")
}
override fun onResponse(call: Call?, response: Response?) {
val body = response?.body()?.string()
val gson = GsonBuilder().create()
val commentsDetail = gson.fromJson(body, Array<Comments>::class.java)
val postDetail = gson.fromJson(body, Array<Posts>::class.java)
runOnUiThread {
for (commentSizeList in 0 until commentsDetail.size) {
val listOfComments = commentsDetail[commentSizeList] // List of all comments by Index
val post = postDetail[postId - 1].id // Post Id
// If postId is equal to original post id
if (listOfComments.postId == post) {
// Print it out
println(listOfComments)
}
}
}
}
})
}
When I print out the list, it really does print out all the comments per particular post based on it's ID (since comparison it's done). However it prints out each comment separately (and setting listOfComments.size prints out 1 for each comment).
displaying list of all comments per post
My question is how to combine them all, so it displays total number of comments per post (in this case -> 5)?
So the way I would do this is like this:
val totalNumberOfCommentsForPost = commentsDetail?.filter {
it.postId == post
}?.size ?: 0
.filter only selects the comments with the same post id.
Then .size gets hows many are in the new filtered list.
And finally as a safety precaution ?: 0 if any of these functions return null then return 0.
If I understand what you need correctly, you could replace your entire for loop with this code using filter:
// Post Id, the way you've been calculating it
val post = postDetail[postId - 1].id
// a list of comments only containing the ones that have the ID you need
val filteredComments: List<Comments> = commentsDetail.filter { it.postId == post }
From here, you can print either the entire list:
println(filteredComments)
Or the size of the list:
println(filteredComments.size)

Generics problems encountered by Kotlin and GSON

I try to write a network request function,Its role is to analyze the general format and provide data content to callbacks.
This is my defined class:
data class Response<T>(
val code: Int = 0,
#JvmSuppressWildcards
var data: ArrayList<T>,
val message: String = "")
in this class 'code' and 'message' is fixed,'data' has different types
Select one of data:
data class TestData(
#SerializedName("create_by")
val createBy: String = "",
#SerializedName("create_time")
val createTime: String = "",
#SerializedName("name")
val name: String = "",
#SerializedName("id")
val id: String = "")
There is my network request function:
fun <T> post(callBack: (ArrayList<T>) -> Unit) {
...
override fun onSuccess(response:Response<String>) {
val result = gson.fromJson<Response<T>>(response.body().reader(),Response::class.java)
when{
result.code==200-> callBack.invoke(result.data)
}
}
...
}
Use it at Activity:
Request.addr(Constants.GET_TEST)
.post<TestData> {
tv.text = it[0].name
}
When i use Gson parse the server returns data,and want use JavaBean ,Logcat throw this Exception:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.network.response.TestData
i tried to use TypeToken to solve this problem ,but it also does not work.
It cause, because the parser can't fetch the real type T at runtime.
This extension works for me
inline fun Gson.fromJson(json: String) =
this.fromJson(json, object: TypeToken() {}.type)!!
Then you need modify as in you methods, as IDE recommend you.

How to parse JSON indexed dictionnary in Kotlin [duplicate]

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.
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")
}
You can use this library https://github.com/cbeust/klaxon
Klaxon is a lightweight library to parse JSON in Kotlin.
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)
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)
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" } ]}
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.
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 :)
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.
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".
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.
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.
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"))
}
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-
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) {
}
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!
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>()
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.
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>;
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()
}
})

Categories

Resources