Why my data can't successful convert to json property I defined - kotlin

I'm try to convert a sqs message to a self defined SQS event, the message in convertMessageFromSqsForEvent is valid and it have value, but the sqsEventProperties is null, what is the problem?
fun convertMessageFromSqsForEvent(message: Message): SQSEventProperties? {
var sqsEventProperties: SQSEventProperties? = null
sqsEventProperties = mapper?.readValue(message.body, SQSEventProperties::class.java)
return sqsEventProperties
}
data class SQSEventProperties(
#JsonProperty("attributes") var attributes: String?,
#JsonProperty("awsRegion") var awsRegion: String?,
#JsonProperty("body") var body: String?,
#JsonProperty("eventSource") var eventSource: String?,
#JsonProperty("eventSourceARN") var eventSourceARN: String?,
#JsonProperty("md5OfBody") var md5OfBody: String?,
#JsonProperty("md5OfMessageAttributes") var md5OfMessageAttributes: String?,
#JsonProperty("messageId") var messageId: String?,
#JsonProperty("receiptHandle") var receiptHandle: String?,
)
message
{MessageId: 94995ca7-ee33-4109-aabf-23d375878140,
ReceiptHandle: AQEBf5kvP0RwvFtKPmZglRhxXAS7FKGxbUCazYXD0+HFq6hpQGri3KU8TZirGC9NEu86DZretaBi5oeFbzzkWnMoxwzZ68/m7FZLt8fxX0gCoL2CxJMy0JXClRTZI+O06Hwn+CZMb5LOAKkGffJUS48dfe5GERKAZGsnLK4vA2pODrxfv1vvC6CXTzejoA9Dw+kuo51F5S86iRzXjItjzRJDxcJp4xeXdjrWps4Wfx233G+x4KiP9t9yZ73L9ucDLzdguDwTee07KG5SdRgHDQWyXcc0IJNtAe/NDmkIhWhigHoZSQmVCVNQFBtcYuB6X2khB+oQZsLz0Vh1NLs3zQVuusBbMg6tpk94N28FUKCIugtwDxBRjIdJqfbUXEeL1PxXCBcb+Pn3vLH9NHKHEclxbg==,
MD5OfBody: de5da762733bf71530cdb2aba2892a65,
Body: Please send request for updated data,
Attributes: {},
MessageAttributes: {}}

The name of the field of the data class must be the same as the key of the Json data. You can modify your data class and try again.
data class SQSEventProperties(
#JsonProperty("Attributes") var attributes: String?,
#JsonProperty("AwsRegion") var awsRegion: String?,
#JsonProperty("Body") var body: String?,
#JsonProperty("EventSource") var eventSource: String?,
#JsonProperty("EventSourceARN") var eventSourceARN: String?,
#JsonProperty("Md5OfBody") var md5OfBody: String?,
#JsonProperty("Md5OfMessageAttributes") var md5OfMessageAttributes: String?,
#JsonProperty("MessageId") var messageId: String?,
#JsonProperty("ReceiptHandle") var receiptHandle: String?,
)

Related

Android studio kotlin error : Execution failed for task ':app:kaptDebugKotlin'

I am trying to implement a room database for a league data but when I'm trying to compile, it's doesnt work. The error seem to be throw in the Database file. I hope you can help me
In the Dao, i select all the countryProperty and insert them in the database,
#Dao
interface CountryDataBaseDao {
#Query("SELECT * FROM DatabaseCountryProperty")
fun getData() : LiveData<List<DatabaseCountryProperty>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertData(data : List<DatabaseCountryProperty>)
}
In the entity, countryProperty entity get the extra entity,
#Entity
data class DatabaseCountryProperty constructor(
#PrimaryKey
val id: String,
val name: String,
val imagePath: String?,
val extra: DatabaseExtraCountry?)
data class DatabaseExtraCountry constructor(
val continent : String?,
val subRegion : String?,
val worldRegion : String?,
val fifa : String?,
val iso : String?,
val iso2 : String?,
val longitude : Double?,
val latitude : Double?,
val flag : String?) {
}
fun List<DatabaseCountryProperty>.asDomainModel() : List<DevByteCountryProperty> {
return map {
DevByteCountryProperty(
id = it.id,
name = it.name,
imagePath = it.imagePath,
extra = DevByteExtraCountry(
continent = it.extra?.continent,
subRegion = it.extra?.subRegion,
worldRegion = it.extra?.worldRegion,
fifa = it.extra?.fifa,
iso = it.extra?.iso,
iso2 = it.extra?.iso2,
longitude = it.extra?.longitude,
latitude = it.extra?.latitude,
flag = it.extra?.flag
)
)
}
}
In the Database,
#Database(entities = [DatabaseCountryProperty::class], version = 1, exportSchema = false)
abstract class DataDataBase : RoomDatabase() {
abstract val countryDataBaseDao : CountryDataBaseDao
}
private lateinit var INSTANCE: DataDataBase
fun getDatabase(context: Context): DataDataBase {
synchronized(DataDataBase::class.java) {
if (!::INSTANCE.isInitialized) {
INSTANCE = Room.databaseBuilder(context.applicationContext,
DataDataBase::class.java,
"data").build()
}
}
return INSTANCE
}
The errors was that I didn't had the annotation embedded to declare the fact that the extra is a sub_class. So the code is,
#Entity
data class DatabaseCountryProperty constructor(
#PrimaryKey
val id: String,
val name: String,
val imagePath: String?,
#Embedded
val extra: DatabaseExtraCountry?)
data class DatabaseExtraCountry constructor(
val continent : String?,
val subRegion : String?,
val worldRegion : String?,
val fifa : String?,
val iso : String?,
val iso2 : String?,
val longitude : Double?,
val latitude : Double?,
val flag : String?) {
}
Add these in your build gradle.
id 'kotlin-kapt' in your plugin
kapt "androidx.room:room-compiler:2.3.0" in your dependencies
Just Do a Try

How to ignore kotlin delegated property using Room ```#Ignore``` annotation

I want to add a delegated property to my Composition class, as follows
#Entity
data class Composition(
val author: String="",
val background: String?,
val description: String="",
val downloadURL: String="",
val duration: String="",
val headPortrait: String?,
#PrimaryKey val id: String,
val isLike: Boolean,
val likeAmount: String="",
val playingAmount: Int=0,
val replyAmount: String?,
val showStyle: String?,
val title: String?,
val userId: String?,
val commentAmount: String?,
val cover: String=""
){
val showDuration by lazy{
val minutes = duration.toInt() /60
val seconds =duration.toInt()%60
"$minutes:$seconds"
}
}
But there gona be a compile error because delegated property can not be saved in database. So i want to
add an
Ignore annotation to this field. What a pity that Androidstuio will throws a complain "This annotation is not applicable to target 'member property with delegate" . Who has ideas for this problem?
As user IR42 points out in his comment, you can use #delegate:Ignore.

Best practice for handling null types in classes when mapping classes together

I am using Kotlin 1.30. I have the following entity classes that will be populated from the API. And some of the properties could be null from the API so I have declared them using the safe null type.
However, I will map this entity class to my domain model class. And just wondering what is the best way to handle the null types?
I have 2 ideas on how to do this when I map the classes.
declare all the equivalent properties as safe null types
use the elivs operator to return either a empty string or a emptylist
In the following snippet I am using the elvis operator. Just wondering what is the best practice for this?
class LoginResponseDomainMapperImp : LoginResponseDomainMapper {
override fun map(entity: LoginResponseEntity): LoginResponse {
return LoginResponse(
entity.token ?: "",
mapUsers(entity.user),
mapEnterprises(entity.enterprises ?: emptyList()),
mapVendors(entity.vendors ?: emptyList()))
}
private fun mapUsers(userEntity: UserEntity?): User {
return User(
userEntity?.id,
userEntity?.email,
userEntity?.firstName,
userEntity?.lastName,
userEntity?.phone,
userEntity?.address,
userEntity?.dob,
userEntity?.customer,
userEntity?.enterpriseIds ?: emptyList(),
userEntity?.vendorIds ?: emptyList())
}
private fun mapEnterprises(enterprisesEntity: List<EnterprisesEntity>): List<Enterprises> {
val enterpriseList = mutableListOf<Enterprises>()
enterprisesEntity.forEach {
val enterprise = Enterprises(
it.id,
it.enterpriseName,
it.typeLabel,
it.country,
it.imageId,
it.managers,
it.members,
it.stripe,
it.locations)
enterpriseList.add(enterprise)
}
return enterpriseList.toList()
}
private fun mapVendors(vendorsEntity: List<VendorsEntity>): List<Vendors> {
val vendorList = mutableListOf<Vendors>()
vendorsEntity.forEach {
val vendor = Vendors(
it.id,
it.vendorName,
it.typeLabel,
it.userRole,
it.latitude,
it.longitude,
it.partner,
it.country,
it.imageId,
it.stripe)
vendorList.add(vendor)
}
return vendorList.toList()
}
}
Entity class that will populate from the API, so any of these could be null
data class LoginResponseEntity(
#SerializedName("token") val token: String?,
#SerializedName("user") val user: UserEntity?,
#SerializedName("enterprises") val enterprises: List<EnterprisesEntity>?,
#SerializedName("vendors") val vendors: List<VendorsEntity>?)
data class UserEntity(
#SerializedName("id") val id: String?,
#SerializedName("email") val email: String?,
#SerializedName("first_name") val firstName: String?,
#SerializedName("last_name") val lastName: String?,
#SerializedName("phone") val phone: String?,
#SerializedName("address") val address: String?,
#SerializedName("dob") val dob: String?,
#SerializedName("customer") val customer: String?,
#SerializedName("enterprise_ids") val enterpriseIds: List<String>?,
#SerializedName("vendor_ids") val vendorIds: List<String>?)
data class EnterprisesEntity(
#SerializedName("id") val id: String?,
#SerializedName("enterprise_name") val enterpriseName: String?,
#SerializedName("type_label") val typeLabel: String?,
#SerializedName("referral_code") val referralCode: String?,
#SerializedName("country") val country: String?,
#SerializedName("image_id") val imageId: String?,
#SerializedName("managers") val managers: List<String>?,
#SerializedName("members") val members: List<String>?,
#SerializedName("stripe") val stripe: Boolean,
#SerializedName("locations") val locations: List<String>?)
data class VendorsEntity(
#SerializedName("id") val id: String?,
#SerializedName("vendor_name") val vendorName: String?,
#SerializedName("type_label") val typeLabel: String?,
#SerializedName("user_role") val userRole: String?,
#SerializedName("latitude") val latitude: Float,
#SerializedName("longitude") val longitude: Float,
#SerializedName("partner") val partner: Boolean,
#SerializedName("country") val country: String?,
#SerializedName("image_id") val imageId: String?,
#SerializedName("stripe") val stripe: Boolean)
Data model class in the domain, Its it better to declare them all safe null types?
data class LoginResponse(
val token: String,
val user: User?,
val enterprises: List<Enterprises>,
val vendors: List<Vendors>)
data class User(
val id: String?,
val email: String?,
val firstName: String?,
val lastName: String?,
val phone: String?,
val address: String?,
val dob: String?,
val customer: String?,
val enterpriseIds: List<String>,
val vendorIds: List<String>)
data class Enterprises(
val id: String,
val enterpriseName: String,
val typeLabel: String,
val country: String,
val imageId: String,
val managers: List<String>,
val members: List<String>,
val stripe: Boolean,
val locations: List<String>)
data class Vendors(
val id: String,
val vendorName: String,
val typeLabel: String?,
val userRole: String,
val latitude: Float,
val longitude: Float,
val partner: Boolean,
val country: String?,
val imageId: String,
val stripe: Boolean)
First of all there are no safe null types in Kotlin. A variable is either nullable or not.
If the API delivers null values, either by absence or by setting them explicitely null, your data classes should reflect that in the way that those variables are nullable (question mark ? right after the data type).
The moment you map those data classes to your entities (domain model) you should handle the null case properly. Since it is a valid case, that the API serves null values, which you expect, you should handle that case by assigning a default value.
Using the elvis operator is just a way to deal with nullable types elegantely, but if you use it, remains your choice.
If you declare all the properties as nullable that's not much better than Java, even when you access them with the null safety operator. If your JSON properties have null value, that means that in your business logic they don't always have a value, and is your responsibility to handle that, by fallback to a default value (i.e. emptyList() or ""), or maybe something more complicated like redirecting to a sign in screen.

moshi custom qualifier annotation to serialise null on one property only

I'd like to serialise null for only one property in my JSON body that is going on a PUT. I don't want to serialize null for any other types in the object. Model class is like this
#Parcel
class User #ParcelConstructor constructor(var college: College?,
var firstname: String?,
var lastname: String?,
var email: String?,
var active: Boolean = true,
var updatedAt: String?,
var gender: String?,
var picture: String?,
var id: String?,
#field: [CollegeField] var collegeInput: String?,
#field: [CollegeField] var otherCollege: String?,)
I only want to serialise collegeInput and otherCollege fields if either of them are null. For example
val user = User(firstname = "foo", lastname=null, collegeInput="abcd", otherCollege = null)
Json will look something like this:
{"user":{
"firstname": "foo",
"collegeInput": "abcd",
"otherCollege": null
}}
Where otherCollege is null, lastname is omitted from the object as by default moshi does not serialise nulls which is what I want, but qualifer fields should be serialized with null values
I tried using
class UserAdapter {
#FromJson
#CollegeField
#Throws(Exception::class)
fun fromJson(reader: JsonReader): String? {
return when (reader.peek()) {
JsonReader.Token.NULL ->
reader.nextNull()
JsonReader.Token.STRING -> reader.nextString()
else -> {
reader.skipValue() // or throw
null
}
}
}
#ToJson
#Throws(IOException::class)
fun toJson(#CollegeField b: String?): String? {
return b
}
#Retention(AnnotationRetention.RUNTIME)
#JsonQualifier
annotation class CollegeField
I added the adapter to moshi but it never gets called
#Provides
#Singleton
fun provideMoshi(): Moshi {
return Moshi.Builder()
.add(UserAdapter())
.build()
}
#Provides
#Singleton
fun provideRetrofit(client: OkHttpClient, moshi: Moshi, apiConfig: ApiConfig): Retrofit {
return Retrofit.Builder()
.baseUrl(apiConfig.baseUrl)
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
}
Your toJson adapter method will return null when the qualified string value is null, and the JsonWriter will not write the null value.
Here is a qualifier and adapter factory to install that will work.
#Retention(RUNTIME)
#JsonQualifier
public #interface SerializeNulls {
JsonAdapter.Factory JSON_ADAPTER_FACTORY = new JsonAdapter.Factory() {
#Nullable #Override
public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Set<? extends Annotation> nextAnnotations =
Types.nextAnnotations(annotations, SerializeNulls.class);
if (nextAnnotations == null) {
return null;
}
return moshi.nextAdapter(this, type, nextAnnotations).serializeNulls();
}
};
}
Now, the following will pass.
class User(
var firstname: String?,
var lastname: String?,
#SerializeNulls var collegeInput: String?,
#SerializeNulls var otherCollege: String?
)
#Test fun serializeNullsQualifier() {
val moshi = Moshi.Builder()
.add(SerializeNulls.JSON_ADAPTER_FACTORY)
.add(KotlinJsonAdapterFactory())
.build()
val userAdapter = moshi.adapter(User::class.java)
val user = User(
firstname = "foo",
lastname = null,
collegeInput = "abcd",
otherCollege = null
)
assertThat(
userAdapter.toJson(user)
).isEqualTo(
"""{"firstname":"foo","collegeInput":"abcd","otherCollege":null}"""
)
}
Note that you should use the Kotlin support in Moshi to avoid the #field: oddities.
Try approach from my gist:
https://gist.github.com/OleksandrKucherenko/ffb2126d37778b88fca3774f1666ce66
In my case I convert NULL from JSON into default double/integer value. You can easily modify the approach and make it work for your specific case.
p.s. its JAVA, convert it to Kotlin first.

Kotlin data class optional variable

data class Student(
val id: Int?,
val firstName: String?,
val lastName: String?,
val hobbyId: Int?,
val address1: String?,
val address2: String?,
val created: String?,
val updated: String?,
...
)
I have like above data class, and I want to create a Student instance with only first name and last name.
So If I run this,
// creating a student
Student(
firstName = "Mark"
lastName = "S"
)
I will get No value passed for parameter 'id' ... errors.
To avoid that, I modified the Student class like this,
data class Student(
val id: Int? = null,
val firstName: String? = null,
val lastName: String? = null,
val hobbyId: Int? = null,
val address1: String? = null,
val address2: String? = null,
val created: String? = null,
val updated: String? = null,
...
)
But it looks so ugly.
Is there any better way?
You can set default values in your primary constructor as shown below.
data class Student(val id: Int = Int.MIN_VALUE,
val firstName: String,
val lastName: String,
val hobbyId: Int = Int.MIN_VALUE,
val address1: String = "",
val address2: String = "",
val created: String = "",
val updated: String = "")
Then you can use named arguments when creating a new student instance as shown below.
Student(firstName = "Mark", lastName = "S")
I am not sure the solution I am giving you is the best or not. But definitely neat.
The only thing I don't like to go with nulls as default param, because Kotlin offers Null Safety, lets not remove it just because to fulfil some other requirement. Mark them null only if they can be null. Else old Java way is good. Initialize them with some default value.
data class Student(val id: Int,
val firstName: String,
val lastName: String,
val hobbyId: Int,
val address1: String,
val address2: String,
val created: String,
val updated: String) {
constructor(firstName: String, lastName: String) :
this(Int.MIN_VALUE, firstName, lastName, Int.MIN_VALUE, "", "", "", "")
}
data class InLog(
var clock_in_lat: String?="None",
var clock_in_lng: String?="None",
var clock_out_lat: String?="None",
val clock_out_lng: String?="None",
val created_at: String?="None",
val duration: String?="None",
val end_time: String?="None",
val id: Int?=Int.MIN_VALUE,
var late_duration: String? = "None",
val start_time: String?="None",
val type: String?="None",
val updated_at: String?="None",
val user_id: Int?=Int.MIN_VALUE)
in Kotlin we do like that remeber ? mark symbol use.