Android Serialization Jackson deserialize using JsonTypeInfo - kotlin

Good morning. I'm trying to figure out how to deserialize a parameter but I can't find the solution. In practice, the response JSON should populate or not a field based on the value I get from another type of field, which is numeric and can be 0 or 1.
In particular (seeing following serialization) in MyData class there is a property called "mutable_value" that can be 1 or 0. when is 1 I must serialize b_property, else I must serialzie c_property.
I'm trying to achieve it but I don't know where I can do my case if 0 or 1...
The class I serialize is the following:
data class ClassResponseData(
#JsonProperty("code")
val code: String,
#JsonProperty("data")
val data: MyData,
) {
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
visible = true,
property = , // <-------------what I must do here?
defaultImpl = MyData.DefaultValue::class
)
#JsonSubTypes(
JsonSubTypes.Type(
value = MyData.BType::class,
name = "new_domain"
),
JsonSubTypes.Type(
value = MyData.CType::class,
name = "new_domain"
)
)
sealed class MyData{
data class DefaultValue(
#JsonProperty("b_value")
val bValue: String,
#JsonProperty("c_value")
val cValue: String,
#JsonProperty("mutable_value")
val mutableValue: Int
)
data class BType(
#JsonProperty("b_value")
val bValue: String,
#JsonProperty("mutable_value")
val mutableValue: Int
) : MyData()
data class CType(
#JsonProperty("c_value")
val cValue: String,
#JsonProperty("mutable_value")
val mutableValue: Int
) : MyData()
}
}
What I'm doing wrong?

Related

How do I write Nested Generics in Kotlin's Sealed Classes

I'm trying to come up with a data model that allows me to do the following:
Define a type of Task and change its Status
This Status can be either InProgress or Completed
In the case of a completed Status, I want to be able to add data, that is specific to the Task that was completed.
Initially, I came up with this data model:
sealed class Task<R : TaskResult> {
abstract val status: TaskStatus<R>
data class A(
val data: String,
override val status: TaskStatus<NoResult>,
) : Task<NoResult>()
data class B(
val data: String,
override val status: TaskStatus<TaskBResult>,
) : Task<TaskBResult>()
}
sealed class TaskStatus<R : TaskResult> {
object InProgress : TaskStatus<NoResult>()
data class Completed<R : TaskResult>(val result: R) : TaskStatus<R>()
}
sealed class TaskResult {
object NoResult : TaskResult()
data class TaskBResult(val resultData: String) : TaskResult()
}
Here you have Task.A and Task.B, where:
A completed Task.A only accepts NoResult
A completed Task.B only accepts TaskBResult
However, when I run this:
fun main() {
val taskA = Task.A(
data = "data",
status = TaskStatus.InProgress
).copy(
status = TaskStatus.Completed(
result = NoResult
)
)
val taskB = Task.B(
data = "data",
status = TaskStatus.InProgress
).copy(
status = TaskStatus.Completed(
result = TaskBResult(
resultData = "resultData"
)
)
)
}
I get the following compile error for setting the initial status of Task.B:
status = TaskStatus.InProgress
Type mismatch.
Required: TaskStatus<TaskResult.TaskBResult>
Found: TaskStatus.InProgress
Does anyone know how to change the data model so I'm allowed to run this (or a very similar) main function?
This could work with a very little change: just make TaskStatus a covariant generic class and make InProgress a TaskStatus<Nothing>. This is a typical strategy you can use when you have "special case" objects that represent no state. After this change, your code should compile:
sealed class Task<R : TaskResult> {
abstract val status: TaskStatus<R>
data class A(
val data: String,
override val status: TaskStatus<TaskResult.NoResult>,
) : Task<TaskResult.NoResult>()
data class B(
val data: String,
override val status: TaskStatus<TaskResult.TaskBResult>,
) : Task<TaskResult.TaskBResult>()
}
sealed class TaskStatus<out R : TaskResult> {
object InProgress : TaskStatus<Nothing>()
data class Completed<R : TaskResult>(val result: R) : TaskStatus<R>()
}
sealed class TaskResult {
object NoResult : TaskResult()
data class TaskBResult(val resultData: String) : TaskResult()
}
fun main() {
val taskA = Task.A(
data = "data",
status = TaskStatus.InProgress
).copy(
status = TaskStatus.Completed(
result = NoResult
)
)
val taskB = Task.B(
data = "data",
status = TaskStatus.InProgress
).copy(
status = TaskStatus.Completed(
result = TaskBResult(
resultData = "resultData"
)
)
)
}

Is there a way to not inherit a specific property?

I'm trying to group my classes like images.
It is a structure in which one header and several items are gathered in one group to form a list.
If the Header property and Item list properties are placed in the sealed class, the subclass inherits this, so the structure becomes strange.
So, I want to make it impossible to inherit only Header property and Item list property in sealed class. Is there a way?
GroupedItem
sealed class GroupedItem(val layoutId : Int = 1) {
val header: Header = Header() // I want to make inheritance impossible
val itemList: List<Item> = listOf() // I want to make inheritance impossible
data class Header(
val id: String = "",
) : GroupedItem(someValue)
data class Item(
val id: String = "",
val set: Int = 1,
) : GroupedItem(someValue)
}
Grouping example
for (i in 0 until headerNumber) {
val headerText = "Header $i"
val header = GroupedItem.Header(headerText)
val itemList = arrayListOf<GroupedItem.Item>()
val indexNumber = Random.nextInt(2, 5)
for (j in 0 until indexNumber) {
val itemText = "Item $j"
itemList.add(GroupedItem.Item(itemText))
}
val groupedItem = GroupedItem(header, itemList)
groupedItemList.add(groupedItem)
}
ADDED
Main
fun main() {
val test = RoutineItem.Header()
println(test.id)
}
ERROR
Exception in thread "main" java.lang.StackOverflowError
at RoutineItem$Header.<init>(hello.kt)
at RoutineItem$Header.<init>(hello.kt:18)
at RoutineItem.<init>(hello.kt:15)
at RoutineItem.<init>(hello.kt)
at RoutineItem$Header.<init>(hello.kt:22)
at RoutineItem$Header.<init>(hello.kt:18)
at RoutineItem.<init>(hello.kt:15)
at RoutineItem.<init>(hello.kt)
``
The way you're defining it, a Header is a GroupedItem, and every GroupedItem has a Header, which is incorrect, right? It seems like you want one of these:
GroupedItem represents a category of items that go in your groups, i.e. Headers and ItemLists:
You can use an interface, but a sealed class is fine for this (and gives you the GroupedItem.Thing namespacing). Since GroupedItem is a general category, it shouldn't contain a Header or anything else, just like a Vehicle shouldn't have a Car property
GroupedItem represents a structure that contains a Header and an ItemList (possibly optional):
In this case, Header isn't a GroupedItem at all (same as a Wheel isn't a Car) and shouldn't inherit from it - it has no relation to that class. GroupedItem just happens to include a Header as a property.
If you want the GroupedItem.Thing naming scheme to keep things organised, you can just make Header a class within GroupedItem (not an inner class though):
class GroupedItem(
val header: Header = Header(),
val itemList: List<Item> = listOf()
){
data class Header(
val id: String = "",
)
data class Item(
val id: String = "",
val set: Int = 1,
)
}
you'll have to sort out where layoutId is coming from though.
honestly though, I think you probably want both of these things:
GroupedItem to represent things that go in groups
Group for an actual concrete group of those things
class Group(
val header: Header = Header()
val itemList: List<Item> = listOf()
)
sealed class GroupedItem(val layoutId : Int = 1) {
data class Header(
val id: String = "",
) : GroupedItem(someValue)
data class Item(
val id: String = "",
val set: Int = 1,
) : GroupedItem(someValue)
}

How to create a customize primary constructor of sub data-class in Kotlin

I have a class structure like below:
abstract class AuthorizeSalesRequestDto {
open val sequenceNumber: Int = 0
open val currentService: String = ""
open val amount: Int = 0
open val taxOthers: Int = 0
open val trainingMode: Boolean = false
}
data class CreditAuthorizeSales(
override val currentService: String = "Credit"
) : AuthorizeSalesRequestDto() {
}
data class UnionPayAuthorizeSales(
override val currentService: String = "UnionPay"
) : AuthorizeSalesRequestDto() {
}
I want to init the CreditAuthorizeSales and UnionPayAuthorizeSales like below:
val creditObject = CreditPayment(
sequenceNumber = 1,
amount = 100,
taxOthers = 10,
trainingMode = true
)
I create class structure because future will be more data class like CreditAuthorizeSales for different service. But I can't figure out a way to make a constructor like above.
Can somebody please help me with the syntax or change the class structure? Thanks in advace!!!
Note: Enviroment
Kotlin v1.4.3

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 data class + Gson: optional field

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")