DTO from json in kotlin - json

I have the following JSON structure that I getting from the server:
{
"a": {
"something": "something",
"something2": {
"test": "12345",
"test2": "1234",
},
"something3": "something3"
},
"b": {
"something": "something",
"something2": {
"test": "12365",
"test2": "12",
},
"something3": "something3"
}
}
I can get more objects than "a" and "b".
I need to create DTO in Kotlin, I tried this DTO:
data class Root(
val root: Map<String, Something>
)
data class Something(
val something: String,
val something3: String,
val something2: Something2
)
data class Something2(
val test: String,
val test2: String
)
I use Retrofit library, it automatically parse JSON data to kotlin data class
I always get null from this DTO, how to write DTO for this JSON structure correctly?

Related

How to serialize fields with varying type?

I have the following data classes to parse JSON. I can parse it easily with the decodeFromString method. However, the Info classes could contain the List<Int> type from time to time along with the Int type so that both are included in a single JSON. How can I handle this variation in serialization?
#Serializable
data class Node (#SerialName("nodeContent") val nodeContent: List<Info>)
#Serializable
data class Info (#SerialName("info") val info: Int)
p.s. The closest question to mine is this one: Kotlinx Serialization, avoid crashes on other datatype. I wonder if there are other ways?
EDIT:
An example is given below.
"nodeContent": [
{
"info": {
"name": "1",
},
},
{
"info": [
{
"name": "1"
},
{
"name": "2"
},
],
},
{
"info": {
"name": "2",
},
}
]
Here is an approach with a custom serializer similar to the link you provided. The idea is to return a list with just a single element.
// Can delete these two lines, they are only for Kotlin scripts
#file:DependsOn("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0")
#file:CompilerOptions("-Xplugin=/snap/kotlin/current/lib/kotlinx-serialization-compiler-plugin.jar")
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.encoding.Decoder
#Serializable
data class Node (val nodeContent: List<Info>)
#Serializable(with = InfoSerializer::class)
data class Info (val info: List<Name>)
#Serializable
data class Name (val name: Int)
#Serializer(forClass = Info::class)
object InfoSerializer : KSerializer<Info> {
override fun deserialize(decoder: Decoder): Info {
val json = ((decoder as JsonDecoder).decodeJsonElement() as JsonObject)
return Info(parseInfo(json))
}
private fun parseInfo(json: JsonObject): List<Name> {
val info = json["info"] ?: return emptyList()
return try {
listOf(Json.decodeFromString<Name>(info.toString()))
} catch (e: Exception) {
(info as JsonArray).map { Json.decodeFromString<Name>(it.toString()) }
}
}
}
Usage:
val ss2 = """
{
"nodeContent": [
{
"info":
{"name": 1}
},
{
"info": [
{"name": 1},
{"name": 2}
]
},
{
"info":
{"name": 2}
}
]
}
"""
val h = Json.decodeFromString<Node>(ss2)
println(h)
Result:
Node(nodeContent=[Info(info=[Name(name=1)]), Info(info=[Name(name=1), Name(name=2)]), Info(info=[Name(name=2)])])

How do I parse the json from thingspeak to get the field value using klaxon on android studio?

I'm using thingspeak and I have successfully got thingspeak to fetch the json data using okhttp but I don't know how to parse it correctly using klaxon.
Here is the code
private fun funButton1() {
println("Attempting to get JSON data!")
val url = "https://api.thingspeak.com/channels/1029606/feeds.json?results=1"
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
println(body)
class feeds (val field1: String)
val result = Klaxon()
.parse<feeds>(body.toString())
textView.text = result
}
override fun onFailure(call: Call, e: IOException) {
println("Failed to execute request!")
}
})
This is the json data from the thingspeak
{
"channel": {
"id": 1029606,
"name": "LED ",
"description": "Acts as a medium for the phone and arduino \r\nRules : 1 = LED ON 0 = LED OFF ",
"latitude": "0.0",
"longitude": "0.0",
"field1": "LED STATUS",
"created_at": "2020-04-01T17:19:03Z",
"updated_at": "2020-04-01T17:20:39Z",
"last_entry_id": 25
},
"feeds": [
{
"created_at": "2020-05-11T02:58:07Z",
"entry_id": 25,
"field1": "1"
}
]
}
Im trying to get the value of field1 which the value is one but I don't know how I'm supposed to do that because im stupid. But I'm hoping that someone could show me how to use klaxon properly to get the json data.
For Klaxon, you'll need to create a class which represent the structure of your JSON.
f.e. if you get a JSON with:
{
"username": "admin",
"password": "admin"
}
you wanna make a class which looks like that:
class myClass(val username:String, val password:String)
Then, you can parse it like you are doing.
For your JSON, you'll need a bigger Class.
For the Sake of simplicity, I'll just make a class for feeds and a class for the channel:
class Feed(val created_at:String, val entry_id: Int, val field1:String)
class Channel(val id: Int, val name: String, val description: String, val latitude: String, val longitude:String, val field1: String, val created_at: String, val updated_at: String, val last_entry_id: Int)
Then you can use this class to parse your JSON:
class Thingspeak(val channel: Channel, val feeds: ArrayList<Feed>)
Please let me know if it worked for you!

Can't handle json tree list using TypeToken + generics with Gson in Kotlin

I have got the json structure as below:
{
"object": "list",
"total": 3,
"data": [
{
"object": "brand",
"id": "15243937043340",
"company": {
"object": "company",
"id": "956936000",
"name": "ABC"
},
"name": "Kindle",
"images": [
"http://www.spacecentrestorage.com/assets/uploads/General/SCS-Slide02-Commercial.jpg"
]
},
{
"object": "brand",
"id": "15243937043340",
"company": {
"object": "company",
"id": "956936000",
"name": "ABC"
},
"name": "Kindle",
"images": [
"http://www.spacecentrestorage.com/assets/uploads/General/SCS-Slide02-Commercial.jpg"
]
},
{
"object": "brand",
"id": "15243937043340",
"company": {
"object": "company",
"id": "956936000",
"name": "ABC"
},
"name": "Kindle",
"images": [
"http://www.spacecentrestorage.com/assets/uploads/General/SCS-Slide02-Commercial.jpg"
]
}
],
"associated": {}
}
And this is my Gson data class mapping :
data class Response (
#SerializedName("object")
val obj: String,
val total: Int,
val data: List<*>,
val associated: Response
)
data class Brand (
#SerializedName("object")
val obj: String,
val id: String,
val name: String,
val images: List<String>,
val company: Company
)
data class Company (
#SerializedName("object")
val obj: String,
val id: String,
val name: String
)
When it comes to extracting the tree as above, I find returned data string becomes Malformed Json and gives MalformedJsonException on $[0].companies.null
I have read about the recursive deserialisation function but it is not working in my case. I resort to deserialise as below , using original method, it causes errors
val response = gson.fromJson(queryResult , Response::class.java)
println("result 2 : $response" )
val dataString = response.data.toString()
println("result 3 : $dataString" )
val brands = Gson().fromJson(dataString, Array<Brand>::class.java).toMutableList()
println("result 4 : $brands" )
I would like to ask :
If returning json component to string, shall all the indents and symbols " be erased ?
To extract all associated object of the elements of the list of objects, what precautions do I have to take for deserialising list of objects using Gson ?
If you set the type parameter of the data list in Response to Brand GSON knows how to deserialise the items of the list.
data class Response (
#SerializedName("object")
val obj: String,
val total: Int,
val data: List<Brand>,
val associated: Response
)
Using this there is no need to parse the items of the list again and you can get all brands like this:
val response = Gson().fromJson(queryResult , Response::class.java)
val dataList = response.data
print("brands: " )
dataList.forEach { println(it) }

How to parse just part of JSON with Klaxon?

I'm trying to parse some JSON to kotlin objects. The JSON looks like:
{
data: [
{ "name": "aaa", "age": 11 },
{ "name": "bbb", "age": 22 },
],
otherdata : "don't need"
}
I just need to data part of the entire JSON, and parse each item to a User object:
data class User(name:String, age:Int)
But I can't find an easy way to do it.
Here's one way you can achieve this
import com.beust.klaxon.Klaxon
import java.io.StringReader
val json = """
{
"data": [
{ "name": "aaa", "age": 11 },
{ "name": "bbb", "age": 22 },
],
"otherdata" : "not needed"
}
""".trimIndent()
data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
val klaxon = Klaxon()
val parsed = klaxon.parseJsonObject(StringReader(json))
val dataArray = parsed.array<Any>("data")
val users = dataArray?.let { klaxon.parseFromJsonArray<User>(it) }
println(users)
}
This will work as long as you can fit the whole json string in memory. Otherwise you may want to look into the streaming API: https://github.com/cbeust/klaxon#streaming-api

How to deserialize json with json-lenses

What is the best practice to deserialize JSON to a Scala case class using json-lenses?
some.json :
[
{
"id": 1,
"name": "Alice"
},
{
"id": 2,
"name": "Bob"
},
{
"id": 3,
"name": "Chris"
}
]
some case class :
case class Foo(id: Long, name: String)
What's best way to convert the json in some.json to List[Foo] ?
json-lenses supports spray-json and with spray-json you could do:
import spray.json._
case class Foo(id: Long, name: String)
object JsonProtocol extends DefaultJsonProtocol {
implicit val FooFormat = jsonFormat2(Foo)
}
import JsonProtocol._
val source = scala.io.Source.fromFile("some.json")
val json = try source.mkString.parseJson finally source.close()
json.convertTo[List[Foo]]
// List[Foo] = List(Foo(1,Alice), Foo(2,Bob), Foo(3,Chris))