Kotlin data class + Gson: optional field - kotlin

I have the following data class in Kotlin:
import com.google.gson.annotations.SerializedName
data class RouteGroup(
#SerializedName("name") var name: String,
#SerializedName("id") var id: Int
)
Sometimes I need to create an object with both fields, sometimes with only one of them.
How can I do this?
EDIT
This is not the duplicate of this question: Can Kotlin data class have more than one constructor?
That question shows how to set a default value for a field. But in my case, I don't need to serialize the field with the default value. I want a field to be serialized only when I explicitly assign a value to it.

it is easy you have to use the nullable operator
import com.google.gson.annotations.SerializedName
data class RouteGroup #JvmOverloads constructor(
#SerializedName("name") var name: String? = null,
#SerializedName("id") var id: Int? = null
)

You may need something like this:
sealed class RouteGroup
data class RouteGroupWithName(
#SerializedName("name") var name: String
) : RouteGroup()
data class RouteGroupWithId(
#SerializedName("id") var id: Int
) : RouteGroup()
data class RouteGroupWithNameAndId(
#SerializedName("name") var name: String,
#SerializedName("id") var id: Int
) : RouteGroup()
EDIT 1:
Or you can use nullable fields and named parameters like this:
data class RouteGroup(
#SerializedName("name") var name: String? = null,
#SerializedName("id") var id: Int? = null
)
val routeGroupWithName = RouteGroup(name = "example")
val routeGroupWithId = RouteGroup(id = 2)
val routeGroupWithNameAndId = RouteGroup(id = 2, name = "example")

Related

Entities and POJOs must have a usable public constructor in Kotlin

I'm trying to make a Room entity by using #Ignore to a list but when i try to build the app i get the following error:
Entities and POJOs must have a usable public constructor
Here is how my code looks like:
#Entity(tableName = "prodotti_table")
data class Prodotto(
#PrimaryKey(autoGenerate = false)
var codart: String,
var desc: String,
var prezzo_acq: Float,
var prezzo_vend: Float,
#Ignore var barcode: List<Barcode>,
var qta: Float
)
#Entity(tableName = "barcode_prodotti_table")
data class Barcode(
#PrimaryKey
var id: Int,
var codart: String,
var barcode: String,
var qta: Float
)
How can i solve it still by using the #Ignore in that data class?
You can't put an ignored property in the constructor, so do this:
#Entity(tableName = "prodotti_table")
data class Prodotto(
#PrimaryKey(autoGenerate = false)
var codart: String,
var desc: String,
var prezzo_acq: Float,
var prezzo_vend: Float,
var qta: Float
) {
#Ignore var barcode: List<Barcode> = emptyList()
}
But remember that a property that isn't in the constructor of a data class will not participate in equals, hashcode, copy, or toString.

Exclude non-null properties when serializing a Kotlin data class

I often will create a data class in Kotlin that is used internally for data models. Example:
data class MyDataModel(
var id: String? = null,
var ownerId: String,
var name: String,
var isPrivate: Boolean = false,
)
I often need to serialize these classes to JSON. The problem is that some of the class properties are not nullable and there are cases where I need to exclude those fields in the serialization. I haven't found a clean and simple way to do that. The solution I currently use is not to use non-nullable properties and then set those that I don't want serialized to null.
Is there another approach?
Solution using kotlinx.serialization:
Define class, including all fields you want to be serialized, mark it as #Serializable
#Serializable
open class MyDataModelSerializable(
open var id: String? = null
)
Make your data class to be its subtype:
data class MyDataModel(
var ownerId: String,
var name: String,
var isPrivate: Boolean = false,
override var id: String? = null
) : MyDataModelSerializable(id)
Serialize instances of MyDataModel class with serializer for MyDataModelSerializable:
val s = serializer<MyDataModelSerializable>()
println(Json.encodeToString(s, MyDataModel(ownerId = "1", name = "2", id = "3", isPrivate = true))) //{"id":"3"}
println(Json.encodeToString(s, MyDataModel(ownerId = "1", name = "2", isPrivate = true))) //{}
println(Json{encodeDefaults = true}.encodeToString(s, MyDataModel(ownerId = "1", name = "2"))) //{"id":null}

Kotlin Data class setter using class fields

I have a redis entity using a kotlin data class and its Id should be a combination of few other fields from the same class.
Can this be achieved by defining setter for Id field instead of computing it outside of data class?
#RedisHash("Game")
data class Game(
#Id
val generatedId: String = "Default_ID",
val name: String,
val location: String,
val homeTeam: String,
val awayTeam: String
)
// want something like this
var generatedId : String = "DEFAULT_ID"
get() = "${name}${location}"
// or even better
var generated_Id : String = "${name}${location}"
Did you try to do something like this?
#RedisHash("Game")
data class Game(
val name: String,
val location: String,
val homeTeam: String,
val awayTeam: String,
#Id
val generatedId: String = "${name}${location}"
)

Kotlin create object with params syntax

I have an object
class Person {
#JsonProperty("name")
var name: String? = null
#JsonProperty("id")
lateinit var id: String}
There is an only empty constructor and I want to create a person so I wrote:
val person = Person()
person.name = "someName"
person.id = "SomeId"
I'm pretty sure that there is a prettier syntax, something like
val person = Person {name = "someName" , id = "someId"}
but I can't find an example.
am I missing something? should I create a secondary constructor to use this syntax or is there another way?
Please check apply method.
Your code will be like this:
val person = Person().apply {name = "someName", id = "someId"}
Another way - you can change declaration of Person to (e.g. just change brackets, replace var to val and remove lateinit):
class Person (#JsonProperty("name") val name: String? = null,
#JsonProperty("id") val id: String )
Then you will able to do this:
val person = Person(name = "someName", id = "someId")
You can achieve it with the constructor parameter.
class Person(
#JsonProperty("name")
var name: String? = null,
#JsonProperty("id")
var id: String
)
val person = Person(name = "someName", id = "someId")
Another way is make your class and desired variables open to be overridden.
open class Person {
#JsonProperty("name")
open var name: String? = null
#JsonProperty("id")
open var id: String = ""
}
val person = object : Person() {
override var name: String? = "SomeName"
override var id = "SomeId"
}

Using Moshi with multiple input fields

I have some JSON that looks like this:
{
"name" : "Credit Card",
"code" : "AUD",
"value" : 1000
}
and am using Moshi to unmarshall this into a data structure like:
data class Account(
#Json(name = "name")
val name: String,
#Json(name = "currency")
val currency: String,
#Json(name = "value")
val value: Int
)
Everything works well. However, I really would like to extract the currency and value parameters into a separate Money object. So my model looks more like:
data class Money(
#Json(name = "currency")
val currency: String,
#Json(name = "value")
val value: Int
)
data class Account(
#Json(name = "name")
val name: String,
#Json(name = "???")
val money: Money
)
The challenge I'm struggling with is how to annotate things so that the Money object can be given two different fields (currency and value) that come from the same level as the parent account.
Do I need to create an intermediate "unmarshalling" object called, say, MoshiAccount and then use a custom adapter to convert that to my real Account object?
I saw How to deseralize an int array into a custom class with Moshi? which looks close (except that in that case, the adapted object (VideoSize) only needs a single field as input... in my case, I need both currency and value)
Any thoughts or suggestions would be much appreciated. Thanks
Moshi's adapters can morph your JSON structure for you.
object ADAPTER {
private class FlatAccount(
val name: String,
val currency: String,
val value: Int
)
#FromJson private fun fromJson(json: FlatAccount): Account {
return Account(json.name, Money(json.currency, json.value))
}
#ToJson private fun toJson(account: Account): FlatAccount {
return FlatAccount(account.name, account.money.currency, account.money.value)
}
}
Don't forget to add the adapter to your Moshi instance.
val moshi = Moshi.Builder().add(Account.ADAPTER).add(KotlinJsonAdapterFactory()).build()
val adapter = moshi.adapter(Account::class.java)