I am using retrofit (Im new to it) and gson to serialize an object into a json to send through an api (POST). The object has a date field, and when serializing it, it is not recognized by the api, which responds with:
{"detail":[{"loc":["body","birth_date"],"msg":"invalid date format","type":"value_error.date"}]}
My class:
data class User(
var name: String = "",
var email: String = "",
#PrimaryKey(autoGenerate = false)
var id: String = UUID.randomUUID().toString(),
var sex: String? = null,
#SerializedName("birth_date")
var birthDate: Date = Date()
)
My call to the API:
#POST("user/me")
suspend fun createUser(#Header(AUTH_HEADER) token: String, #Body user: User): Response<User>
By the API documentation, the date format should be:
"birth_date": "2022-12-22"
How can i change the value of birthDate to match the api format on the request body?
As you can see
"birth_date": "2022-12-22"
above date value is in String format.
try by changing the data type Date to String
data class User(
....
#SerializedName("birth_date")
var birthDate: String = ""
)
Hope it will work :)
Related
An API is returning responses without any outlying data fields, but instead only as an array object like this:
[{"name":"John Smith", "age":"44", "address":"1 Main Street, Anywhere, USA"}, {"name":"Jane Taylor", "age":"22", "address":"10 Suburbia Lane, Sometown, USA"},{"name":"Simon Jones", "age":"36", "address":"33 City Boulevard, Midvalley, USA"}]
My response data classes usually include labels, like this:
data class MyResponseClass (
val status: String? = null,
val description: String? = null
)
How should I structure a response class that does not include labels?
TIA.
UPDATE:
Following Ivo's answer (thank you, btw), how could such a class inherit a base class?
open class MyBaseReponseClass() : Serializable {
val status: String? = null
val description: String? = null
}
data class MyResponseClass (
val name: String? = null,
val age: String? = null,
val address: String? = null
) : MyBaseReponseClass()
The name, age, and address are in an array, but not status and description.
Thanks again!
Not sure what you mean with not using labels. it clearly has name, age and address so it will be
data class MyResponseClass (
val name: String? = null,
val age: String? = null
val address: String? = null
)
And where you indicate the type of the response you say it's a List<MyResponseClass>
I am working on an android application that uses Firebase Firestore as a database. I am trying to store a list of SoldItems where each soldItem has a PrescriptionType formed from Sealed classes.
data class SoldItem(var itemName : String = "",
var quantity : String = "",
var totalPrice : Int = 0,
var itemQuantityType: ItemQuantityType = ItemQuantityType.SINGLE,
var prescriptionType: String = ""
) : Serializable
prescriptionType while defined as a string is originally a Sealed Class PrescriptionType
sealed class PrescriptionType : Parcelable {
// Check how to parcelize objects
#Parcelize
data class Syrup(var quantityInMls : Double = 2.5, var frequency : PrescriptionFrequency = PrescriptionFrequency.Stat, var time : PrescriptionTime = PrescriptionTime.OneDay) : PrescriptionType()
#Parcelize
data class Lotion(var frequency : PrescriptionFrequency = PrescriptionFrequency.Stat, var time : PrescriptionTime = PrescriptionTime.OneDay): PrescriptionType()
#Parcelize
data class Capsule(var quantity : Int = 1, var frequency : PrescriptionFrequency = PrescriptionFrequency.Stat, var time : PrescriptionTime = PrescriptionTime.OneDay ) : PrescriptionType()}
I store prescriptionType as a string because of the lack of support in FirebaseFirestore android SDK directly for sealed classes. I've had to create my own TypeConverter. This works quite well in generating a string (JSON) from each prescriptionType and also generating a prescriptionType from the string.
#JvmStatic
#TypeConverter
fun fromPrescription(prescriptionType: PrescriptionType) : String{
val prescriptionAdapterFactory : RuntimeTypeAdapterFactory<PrescriptionType> = RuntimeTypeAdapterFactory.
of(PrescriptionType::class.java, "type")
.registerSubtype(PrescriptionType.Syrup::class.java, "Syrup")
.registerSubtype(PrescriptionType.Lotion::class.java, "Lotion")
.registerSubtype(PrescriptionType.Capsule::class.java, "Capsule")
val gsonBuilder = GsonBuilder().registerTypeAdapterFactory(prescriptionAdapterFactory).create()
return "\"" +gsonBuilder.toJson(prescriptionType, PrescriptionType::class.java) + "\""
}
#JvmStatic
#TypeConverter
fun toPrescriptionType(prescriptionType : String) : PrescriptionType {
val prescriptionAdapterFactory : RuntimeTypeAdapterFactory<PrescriptionType> = RuntimeTypeAdapterFactory.
of(PrescriptionType::class.java, "type")
.registerSubtype(PrescriptionType.Syrup::class.java, "Syrup")
.registerSubtype(PrescriptionType.Lotion::class.java, "Lotion")
.registerSubtype(PrescriptionType.Capsule::class.java, "Capsule")
val gsonBuilder = GsonBuilder().registerTypeAdapterFactory(prescriptionAdapterFactory).create()
return gsonBuilder.fromJson(prescriptionType, PrescriptionType::class.java)
}
}
Storing the string works quite well in the database. The object is stored and everything is fine - we can see that the prescriptionType is a string.
However in retrieving soldItems in Firebase, we encounter an issue where it seems the firebase SDK returns the JSON string and converts it to an object and hence crashes as prescriptionType is a String not an object.
val soldItemsType = object : TypeToken<List<SoldItem>>() {}.type
val soldItemsFromBackend = it.get("soldItems").toString()
Timber.d(soldItemsFromBackend)
val soldItems = Gson().fromJson<List<SoldItem>>(
soldItemsFromBackend,
soldItemsType
)
Logging soldItemsFromBackend gives
[{drugQuantityType=SINGLE, prescriptionType={"type":"Capsule","frequency":"Stat","quantity":1,"time":"OneDay"}, itemName=Docusate, quantity=2, totalPrice=0}]
and trying to create soldItems using Gson gives an error
Caused by: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 46 path $[0].prescriptionType
I think Gson wants to parse prescriptionType as a string but instead sees an object. I've tried to pad the prescriptionType string with extra quotes but it still didn't work.
Does anyone please know how I can retrieve prescriptionType as a string?
I have a simple Json class that handles retrofit perfectly.
But there is no timestamp in it.
I add my custom field which is not in json, but it is always null
//Custom field for timestamp
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
//My class for JSON respons
#Entity(tableName = "nal")
data class CurrencyItem(
#PrimaryKey(autoGenerate = true)
val id:Long,
#SerializedName("base_ccy")
var baseCcy: String,
#SerializedName("buy")
val buy: String,
#SerializedName("ccy")
val ccy: String,
#SerializedName("sale")
val sale: String,
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
)
Simplest solution add custom field in model for timestamp.
val timeStamp: String
Аnd when retrofit response come, rewrite this null field with timestamp, i use method SimpleDateFormat
// retrofit response
var res = resp.body()
// new list which i create and rewrite with timestamp and then return
var l = ArrayList<CurrencyItem>()
//read response list with json converted data
for (r in res.orEmpty()){
l.add(CurrencyItem(1 ,
r.baseCcy,
r.buy,
r.ccy,
r.sale,
SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())))
}
//return this new list with timestamp. I use repository pattern with livedata
response.value = l
I have one issue about code data class kotlin android.
How to implement server response? sometimes I get String value or sometime get Object class.
class CMSRespTemp {
data class CMSRespApi(
val status: Boolean = false,
val message: String = "",
val data: String as Data
)
data class Data(
val cms_id: String = "",
val cms_content: String = ""
)
}
When I implement only Data class it works, like this val data: Data or val data: String. But I need together Data and String with key only data.
Is it possible?
When having multiple type for same variable, we can use Any type which is equivalent to Object type in java. So solution is like below :
class CMSRespTemp {
data class CMSRespApi(
val status: Boolean = false,
val message: String = "",
var data: Any? = null // changed it to var from val, so that we can change it's type runtime if required
)
data class Data(
val cms_id: String = "",
val cms_content: String = ""
)
}
And when accessing that variable, one can simply cast like below :
val apiResponse : CMSRespApi //= some API response here from network call
when (apiResponse.data) {
is String -> {
// apiResponse.data will be smart-casted to String here
}
else -> {
val responseData = Gson().fromJson<CMSRespApi.Data>(
Gson().toJsonTree(apiResponse.data),
CMSRespApi.Data::class.java
)
}
}
After 12 Hrs spend and got the solution my self,
val getResultCon = getSerCont.result // response Any
val gson = Gson()
val jsonElement = gson.toJsonTree(getResultCon)
val resultData = gson.fromJson(jsonElement, SearchContactApi.Result::class.java)
Convert your data string to toJsonTree and fromJson with model class then got result.
Hi I have a Kotlin data class as follows
data class User (
#get:Exclude val gUser: Boolean,
#get:Exclude val uid: String,
#get:PropertyName("display_name") val displayName: String,
#get:PropertyName("email") val email: String,
#get:PropertyName("account_picture_url") val accountPicUrl: String,
#get:PropertyName("provider") val provider: String
)
I am able to serialize the object without an issues. But i'm having trouble deserializing the object when doing a firebase query. Currently this is what i'm doing to get the data
_firebaseReference.child(getString(R.string.firebase_users_key)).child(user.uid)
.setValue(user).addOnCompleteListener{
_firebaseReference.child("users").child(user.uid)
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
val userHash = p0.value as HashMap<*, *>
var currentUser: User
if (userHash[getString(R.string.provider_key)]
!= getString(R.string.provider_google)) {
currentUser = User(false, p0.key!!,
userHash["display_name"].toString(),
userHash["email"].toString(),
userHash["account_picture_url"].toString(),
userHash["provider"].toString())
} else {
currentUser = User(true, p0.key!!,
userHash["display_name"].toString(),
userHash["email"].toString(),
userHash["account_picture_url"].toString(),
userHash["provider"].toString())
}
}
}
})
}
This is only a test project that i'm working on to practice my Kotlin, but this is something I would like to figure out.
If i'm doing it completely wrong please let me know, any advise would be greatly appreciated
Thanks
Firebase needs an empty constructor to be able to deserialize the objects:
data class User(
#Exclude val gUser: Boolean,
#Exclude val uid: String,
#PropertyName("display_name") val displayName: String,
#PropertyName("email") val email: String,
#PropertyName("account_picture_url") val accountPicUrl: String,
#PropertyName("provider") val provider: String
) {
constructor() : this(false, "", "", "", "", "")
}
You can either declare it like so and provide some default values to be able to call the primary constructor or you can declare default values for all your parameters:
data class User (
#Exclude val gUser: Boolean = false,
#Exclude val uid: String = "",
#PropertyName("display_name") val displayName: String = "",
#PropertyName("email") val email: String = "",
#PropertyName("account_picture_url") val accountPicUrl: String = "",
#PropertyName("provider") val provider: String = ""
)
Then various constructors will be created for you, including an empty constructor.
If there's a problem with serialization there might be because of the getters and setters generated by the ide, try reinforcing them with #get and #set annotations:
data class User (
#Exclude val gUser: Boolean = false,
#Exclude val uid: String = "",
#set:PropertyName("display_name")
#get:PropertyName("display_name")
var displayName: String = "",
#PropertyName("email") val email: String = "",
#set:PropertyName("account_picture_url")
#get:PropertyName("account_picture_url")
var accountPicUrl: String = "",
#PropertyName("provider") val provider: String = ""
)
What I actually wanted is a Kotlin data class which is derived from a domain model interface like so
data class Dto(#PropertyName("serialized_title") val override title: String) : DomainModel
In this case DomainModel is defined this way
interface DomainModel { val title: String }
My goal was to fetch data from Firestore and get deserialized Dto objects which are provided to clients which receive objects of type DomainModel. So this solution above unfortunately didn't work. I saw the workarounds using #get: and #set: Annotations but I wanted my data class properties to be immutable. Simply using vars is a bad design decision in my use case. And also this solution looks quite ugly...
After inspecting the decompiled Java-Code I came up with this solution
data class Dto(
#field:[JvmField PropertyName("serialized_title")]
override val title: String = "") : DomainModel
The decompiled Java-Code simply uses title as public final field having the PropertyName annotation.
I prefer this solution since it doesn't violate certain design decisions I made...
In Android Studio (kotlin)
use this (only var and getter and setter):
#set:PropertyName("email") #get:PropertyName("email") var emailPerson: String = ""
None of this works:
#PropertyName("email") var emailPerson: String = ""
#PropertyName("email") val emailPerson: String = ""
#get:PropertyName("email") val emailPerson: String = ""
Android Studio 4.1.2. Gradle: com.google.firebase:firebase-database:19.6.0