How to use json array in Kotlin Using Moshi - android

[{"id":1,"first_name":"Lillis","last_name":"Hawgood"," cars":[ {"item":"Savana 1500"}, {"item":"Vibe"}, {"item":"Estate"} ]}]
data class MyData( val id: Int = 0, val first_name: String = "", val last_name: String = "", val cars: List
)
class Car { #Json(name = "item") var item: String? = null How to use Item in Data class the how ot print in main clas using moshi txtResult.text = "" for (myDataLst in myDataList ?: emptyList()) { txtResult.append("${myDataLst.first_name} - ${myDataLst.last_name} - ${myDataLst.cars} \n") }enter code here
I tried this way only first name and last showing but for cars showing some worng infor

Try this data classes i transform it using Json to Data Class plugin.
data class MyData(
val cars: List<Car>,
val first_name: String,
val id: Int,
val last_name: String
)
data class Car(
val item: String
)
class car : ArrayList<MyData>()

Firstly, please format your code, it's hard to read your code.
Secondly, you can use the Moshi like this:
#JsonClass(generateAdapter = true)
data class MyData(
#Json(name = "id") val id: String,
#Json(name = "first_name") val firstName: String,
#Json(name = "last_name") val lastName: String,
#Json(name = "cars") val cars: List<Car>
)
#JsonClass(generateAdapter = true)
data class Car(
#Json(name = "item") val item: String
)

Related

How to make post request with retrofit + moshi

I'm trying to make a post with retrofit and moshi but keep getting the error mess
com.squareup.moshi.JsonDataException: Expected BEGIN_OBJECT but was STRING at path $
I can't seem to understand why this is so. This is a sample of the json tested on postman:
{
"customerName": "Name",
"customerPhoneNo": "090000000",
"customerAddress": "Address",
"note": "Please",
"items" : [{
"productUid": "5633e1f1-8b00-46de-b73e-43799245a4e8",
"quantity" : "3"
},{
"ProductUid": "fca3ffb1-0130-4e47-b499-721d046c1e32",
"Quantity" : "5"
},
{
"ProductUid": "6a7f3e24-03ff-408a-b67e-8530d411390c",
"Quantity" : "2"
}]
}
My data classes are set up like so:
#Parcelize
data class Order(
val items: List<Item>?,
val customerName: String,
val customerPhoneNo: String,
val customerAddress: String,
val note: String
) : Parcelable
and
#Parcelize
data class Item(
val productUid: String,
var quantity: Int
) : Parcelable
Service utils look like:
interface ProductService {
#Headers("Content-Type: application/json")
#POST("/api/order/saveorder")
suspend fun postProds(#Body order: Order
): Response<Order>
#GET("/api/product/allproducts")
suspend fun getProds(): Response<List<ProdsItem>>
}
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
object Network {
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshi)
.asLenient()
)
.build()
object ProdsApi {
val retrofitService: ProductService by lazy {
retrofit.create(ProductService::class.java)
}
}
}
The sendOrder function is set up like so:
suspend fun sendOrder(order: Order) {
withContext(Dispatchers.Main){
try {
val orderResponse = Network.ProdsApi.retrofitService.postProds(
order )
}
catch (e: Exception) {
Timber.e(e)
}
}
}
The GET request works perfectly.
Any help on this would be appreciated please.
In your Item Data Class you are using quantity as an Int but if you see the Postman JSON response it is a String.
So your class should be like:
data class Item(
#Json(name = "productUid")
val productUid: String?,
#Json(name = "quantity")
var quantity: String
)
Also as I see the key in your JSON response are written in two different ways.
For example your "Product ID" is written as "productUid" in one of the object and is written as "ProductUid" in another object.
So your complete Item Data Class should more look like this :
data class Item(
#Json(name = "productUid")
val productUid: String?,
#Json(name = "ProductUid")
val productUid: String?,
#Json(name = "quantity")
val quantity: String?,
#Json(name = "Quantity")
val quantity: String?
)
Add to app/build.gradle
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.13.0"
Refactor your data class
check the key in your item and replace with right one
if productUid or ProductUid
quantity or Quantity
#JsonClass(generateAdapter = true)
data class Item(
#Json(name = "productUid")
val productUid: String,
#Json(name = "quantity")
var quantity: String
)
#JsonClass(generateAdapter = true)
data class Order(
#Json(name = "items")
val items: List<Item>,
#Json(name = "customerName")
val customerName: String,
#Json(name = "customerPhoneNo")
val customerPhoneNo: String,
#Json(name = "customerAddress")
val customerAddress: String,
#Json(name = "note")
val note: String
)
and try it again

Kotlin Using Genrics for Dynamic Response Parsing

I had the response structure given below
#JsonClass(generateAdapter = true)
data class ResponseMessage(
#field:Json(name = "ClientID") val clientID: String,
#field:Json(name = "MessageID") val messageID: String,
#field:Json(name = "Payload") val payload: Payload,
#field:Json(name = "Timeout") val timeout: Int,
#field:Json(name = "Timestamp") val timestamp: Long,
#field:Json(name = "Type") val type: String
)
#JsonClass(generateAdapter = true)
data class Payload(
#field:Json(name = "Action") val action: String,
#field:Json(name = "Params") val params: Params?
)
#JsonClass(generateAdapter = true)
data class Params(
#field:Json(name = "room") val userDetails: UserDetails?,
#field:Json(name = "configuration") val configurationDetails: ConfigurationDetails?,
#field:Json(name = "category") val category: String?
)
Here Params could be changed in response as per the requested service. However in some cases params will not required at all. How this can be done by making Params as Generic Type so that when i need UserDetails i will pass that and likewise for other params.

how can create TypeConverter for RoomDatabase

in my project , I have two Data Model class
first :
#Entity(tableName = TABLE_TASK)
data class TaskEntity(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "task_id") val taskId: Int,
#ColumnInfo(name = "task_title") val taskTitle: String?,
#ColumnInfo(name = "task_desc") val taskDesc: String?,
#ColumnInfo(name = "task_priority") val taskPriority: Int?,
#ColumnInfo(name = "task_status") val taskStatus: String?,
#ColumnInfo(name = "task_time") val taskTime: String?,
#ColumnInfo(name = "task_notify") val taskNotify: String?
)
and second :
data class SubtaskModel(
val subTasktitle : String?,
val subTaskStatus : Boolean?
)
and I wanted to add a filed in my first Data Model class as the code below (the last field ) :
#Entity(tableName = TABLE_TASK)
data class TaskEntity(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "task_id") val taskId: Int,
#ColumnInfo(name = "task_title") val taskTitle: String?,
#ColumnInfo(name = "task_desc") val taskDesc: String?,
#ColumnInfo(name = "task_priority") val taskPriority: Int?,
#ColumnInfo(name = "task_status") val taskStatus: String?,
#ColumnInfo(name = "task_time") val taskTime: String?,
#ColumnInfo(name = "task_notify") val taskNotify: String?,
#ColumnInfo(name = "task_subtask") val taskSubTask: MutableList<SubtaskModel>?
)
the error that i have got :
Cannot figure out how to save this field into database. You can
consider adding a type converter for it.
so I read(developer.android.com) and I did search about this but I couldn't figure out how can write Type Converter.
thank you
Follow below steps :
Step1. : create a convertor class -
class MyConverter {
#TypeConverter
fun listToJson(value: List< SubtaskModel >?): String = Gson().toJson(value)
#TypeConverter
fun jsonToList(value: String) = Gson().fromJson(value, Array<SubtaskModel>::class.java).toList()
}
Step 2 -> Add converter to your dataBase class
#Database(entities = [TaskEntity::class], version = 1)
#TypeConverters(MyConverter::class)
abstract class myDataBase : RoomDatabase() {
abstract fun myDao(): MyDao
}
That's it.

Moshi expected BEGIN_OBJECT but was BEGIN_ARRAY at path $

This is not retrofit but manual parsing in a Firebase message handler service. I am using KotlinJsonAdapterFactory() when building my Moshi instance.
For some reason it thinks one of the nodes is an array when I am asking for it to be parsed as an object. Here is the JSON:
[
{
"$": {
"updateOrigin": "CIS",
"requestSource": "at20",
"requestID": "0000000000004144"
},
"TS": [{
"$": {
"rid": "202008207681819",
"uid": "L81819",
"ssd": "2020-08-20"
},
"Location": [{
"$": {
"tpl": "PADTON",
"wtd": "17:28",
"ptd": "17:28"
},
"dep": [{
"$": {
"et": "17:28",
"src": "Darwin"
}
}
],
"plat": [{
"_": "2",
"$": {
"platsup": "true",
"cisPlatsup": "true",
"platsrc": "M"
}
}
]
}
]
}
]
}
]
And here are my Data classes:
package com.cniekirk.traintimes.model
import com.squareup.moshi.Json
data class PushPortMessage(
val pushPortMessageItems: List<PushPortMessageItem>?
)
data class PushPortMessageItem(
#Json(name = "TS")
val tS: List<TS>?,
#Json(name = "$")
val messageAttrs: MessageAttrs?
)
data class TS(
#Json(name = "LateReason")
val lateReason: List<String>?,
#Json(name = "Location")
val location: List<Location>?,
#Json(name = "$")
val tsAttrs: TSAttrs?
)
data class MessageAttrs(
#Json(name = "updateOrigin")
val updateOrigin: String?
)
data class Location(
#Json(name = "arr")
val arr: List<Arr>?,
#Json(name = "dep")
val dep: List<Dep>?,
#Json(name = "pass")
val pass: List<Pass>?,
#Json(name = "plat")
val plat: List<Plat>?,
#Json(name = "$")
val stationAttrs: StationAttrs?
)
data class TSAttrs(
#Json(name = "rid")
val rid: String?,
#Json(name = "ssd")
val ssd: String?,
#Json(name = "uid")
val uid: String?
)
data class Arr(
#Json(name = "$")
val arrPassAttrs: List<ArrPassAttrs>?
)
data class Dep(
#Json(name = "$")
var depAttrs: DepAttrs?
)
data class Pass(
#Json(name = "$")
val arrPassAttrs: ArrPassAttrs?
)
data class Plat(
#Json(name = "_")
val platform: Int?,
#Json(name = "$")
val platAttrs: PlatAttrs?
)
data class PlatAttrs(
#Json(name = "platsup")
val platsup: Boolean?,
#Json(name = "cisPlatsup")
val cisPlatsup: Boolean?
)
data class StationAttrs(
#Json(name = "pta")
val pta: String?,
#Json(name = "ptd")
val ptd: String?,
#Json(name = "tpl")
val tpl: String?,
#Json(name = "wta")
val wta: String?,
#Json(name = "wtd")
val wtd: String?,
#Json(name = "wtp")
val wtp: String?
)
data class ArrPassAttrs(
#Json(name = "delayed")
val delayed: String?,
#Json(name = "et")
val et: String?,
#Json(name = "src")
val src: String?
)
data class DepAttrs(
#Json(name = "delayed")
val delayed: String?,
#Json(name = "et")
val et: String?,
#Json(name = "etUnknown")
val etUnknown: String?,
#Json(name = "etmin")
val etmin: String?,
#Json(name = "src")
val src: String?
)
I may just be being stupid but I can't find the issue. Here is how I'm parsing the JSON:
val pushPortMessage = data["body"]
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val pushPortAdapter = moshi.adapter(PushPortMessage::class.java)
pushPortMessage?.let {
val msg = pushPortAdapter.fromJson(pushPortMessage)
println(msg)
}
you are parsing a list of objects not object so the parsing should be something like this
val pushPortMessage = data["body"]
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val listType = Types.newParameterizedType(List::class.java, PushPortMessageItem::class.java)
val adapter: JsonAdapter<List<PushPortMessageItem>> = moshi.adapter(listType)
val pushPortMessageList = adapter.fromJson(pushPortMessage )

Moshi deserializing values as null

I've recently switched from Gson to Moshi and am having trouble parsing some Json.
{
"access_token": "-LNe2LQ7DQH5Y2zs_W5iUumKuaUE",
"token_type": "bearer",
"device_id": "461f-837e-af5050c92fe9",
"expires_in": 3600,
"scope": "*"
}
And here's the model class:
data class AuthToken(
#Json(name = "access_token") val accessToken: String,
#Json(name = "token_type") val tokenType: String,
#Json(name = "device_id") val deviceId: String,
#Json(name = "expires_in") val expiresIn: Int,
#Json(name = "scope") val scope: String
)
Whenever I switch to using Moshi in my retrofit client, I receive the following error:
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull
I have made the field nullable, however it is always deserialized as null. I've checked my retrofit response and it's (obviously) the same when using either Gson or Moshi. What am I doing wrong?
For some reason, when I explicitly tell the AuthToken class to generate an adapter - I receive no null values.
#JsonClass(generateAdapter = true)
data class AuthToken(
#Json(name = "access_token") val accessToken: String,
#Json(name = "token_type") val tokenType: String,
#Json(name = "device_id") val deviceId: String,
#Json(name = "expires_in") val expiresIn: Int,
#Json(name = "scope") val scope: String
)

Categories

Resources