Convert attributes from one data class to another data class (subclasses) - kotlin

hello) . Need some help.I know how to transfer data from A class to B class, but i dont know how to transfer data from subclass A.A1 - which included A class, to subclass B.B1 which included B class.
My A class:
data class SpaceXProperty(
val flight_number: Int,
val launch_year: Int,
val links: Links
){
data class Links(val mission_patch: String?)
}
My B class:
data class Models(val flightNumber: Int,
val mlinks: Mlinks
)
{
data class Mlinks(val mission_patch: String?)
}
I created next function
fun List<SpaceXProperty>.asDomainModel(): List<Models>{
return map {
Models(
flightNumber = it.flight_number,
llinks = it.links //here i got error type mismatch "required: Models.Mlinks found: SpaceXProperty.Links"
)
}
}
So thats to avoid this - i change B class to next
data class Models(val flightNumber: Int,
val llinks: SpaceXProperty.Links
)
I new at kotlin, so dont know how correctly mapping data from one subclass to another. Help please.
i trying to put next function at fun List.asDomainModel(), but its dont work.
fun List<SpaceXProperty.Links>.asDDomainmodel(): List<Models.Llinks>{
return map {
Models.Llinks(
mission_patch = it.mission_patch) }
}
Totally i getting data from "https://api.spacexdata.com/v3/launches" and want paste this data to another data class like "repository". Here Moshi cannot parse nullable i get help that i need to use subclass, but at next step i got problems.

Couple of minor errors:
In the first asDomainModel function you used llinks property instead of mlinks
Trying to assign object of Links to Mlinks
Following code resolves the issue
fun List<SpaceXProperty>.asDomainModel() = map {
Models(
flightNumber = it.flight_number,
mlinks = Models.Mlinks(it.links.mission_patch)
)
}
The original classes stays as is
data class SpaceXProperty(
val flight_number: Int,
val launch_year: Int,
val links: Links) {
data class Links(val mission_patch: String?)
}
data class Models(val flightNumber: Int, val mlinks: Mlinks) {
data class Mlinks(val mission_patch: String?)
}

Related

Subtypes not being recognized in Subclasses

I have the following code setup;
abstract class GenericQuestionEditor() {
protected abstract var data: GenericQuestionData
}
but then when I create EditorSimple() it throws an error when I try to set data to DataSimple(), why?
class EditorSimple(): GenericQuestionEditor() {
override var data = DataSimple()
}
my GenericQeustionData and DataSimple() are setup like this;
abstract class GenericQuestionData {}
class DataSimple: GenericQuestionData() {}
it doesn't complain if I create this function in GenericQuestionEditor()
fun test() {
data = DataSimple()
}
Why do I get an error on data in EditorSimple()? It should recognize it as a subtype and it should be allowed as I understand.
I feel like the answer is found in the kotlin documentation but i'm not sure how to configure it in this case since they are not passed values or part of a collection.
You need to specify the type explicitly:
class EditorSimple(): GenericQuestionEditor() {
override var data: GenericQuestionData = DataSimple()
}
Without the type annotation, the type of data would be inferred to be DataSimple, which doesn't match the type of its super class' data. Even though the types are related, you can't override writable a property with a subtype. Imagine if I did:
class SomeOtherData: GenericQuestionData()
val editor: GenericQuestionEditor = EditorSimple()
editor.data = SomeOtherData() // data is of type GenericQuestionData, so I should be able to do this
But, editor actually has a EditorSimple, which can only store DataSimple objects in data!

How do I use an enum within a kotlin data class

I have this model
data class HourlyModel(
val time: String,
#DrawableRes val image: Int,
val temp: Double
)
I realized that the server will provide me with weather codes which translate to the icon that will be displayed. I think if I pull the #DrawableRes into an enum, it maybe better because I have a model for Today's Weather and WeeklyWeather forecasts.
All 3 models will be using the same weather codes.
I am new to Kotlin but I think if I have an enum class, I should be able to somehow use this within each model
enum class WeatherTypes (
val weatherCode: Int,
#DrawableRes val drawable: Int
) {
SUNNY(0, R.drawable.sunny_t),
RAIN(1,R.drawable.rain_t);
companion object {
fun weatherToImage(weatherCode: Int) = when(weatherCode) {
0 -> SUNNY
1 -> RAIN
else -> SUNNY
}
}
}
Can someone help me out and tell me what I should do to my model to use this enum class to replace the #DrawableRes? if I can't then what is the best option for me?
I'll assume you have different models for different layers. Let's say you have a data class for the data you receive from the server.
data class HourlyDto(
val time: String,
val weatherCode: Int,
val temp: Double,
)
Your domain model will be something like so:
data class HourlyModel(
val time: String,
val weatherType: WeatherType,
val temp: Double,
)
I've refactored your enum class:
enum class WeatherType(
#DrawableRes val imageResId: Int,
) {
SUNNY(R.drawable.sunny_t),
RAIN(R.drawable.rain_t);
companion object {
fun from(weatherCode: Int): WeatherType {
return WeatherType.values()[weatherCode]
}
}
}
Now, you can map your remote model to your domain model. Let's create an extension function for doing that( for the sake of example. You may be doing it another way )
fun HourlyDto.mapToModel(): HourlyModel {
return HourlyModel(
time,
WeatherType.from(weatherCode),
temp
)
}
Finally, you can use you can get your drawable resource-id like so:
val hourlyWeather: HourlyModel = ...
hourlyWeather.weatherType.imageResId
Note: This answers the question of how to use the enum in your model class, but I guess to solve this specific issue you could use original model(with drawable resource-id) and then create a helper function that takes in weathercode and returns the drawable resource-id and reuse that wherever required.

Kotlin: Function declaration must have a name

Aim of the code: Class Pair can print out Product name and quantity, Product name stored in Class Product
class Pair<T, U>(var product: Product, var quantity: Int) {
for ( (product,quantity) in productAndQuantityList) {
println("Name: ${product.productName}")
println("Quantity: $quantity")
}
}
Above Error:(2, 9) Kotlin: Expecting member declaration
Error:(2, 57) Kotlin: Function declaration must have a name
class ShoppingCart{
private val productAndQuantityList = mutableListOf<Pair<Product,Int> >()
...
}
open class Product(
val productName: String,
var basePrice: Double,
open val salesPrice: Double,
val description: String) {
...}
may i know how to change my code?
after class Pair was suggested by Compiler, but should i fill in anything?
Which topic should i work for, to avoid the same errors again?
Thanks!
If you want to run the for loop when the object is instantiated, then you should use an initializer. You can't simply put statements inside class definitions directly.
class Pair<T, U>(var product: Product, var quantity: Int) {
init {
for ( (product,quantity) in productAndQuantityList) {
println("Name: ${product.productName}")
println("Quantity: $quantity")
}
}
}
However, this code is wrong because Pair does not have access to productAndQuantityList, although ShoppingCart does. As Mathias Henze suggested, you should make a function in ShoppingCart and move the for loop into that, like this:
fun printProducts() {
for ( (product,quantity) in productAndQuantityList) {
println("Name: ${product.productName}")
println("Quantity: $quantity")
}
}
As for your Pair class, the type parameters T and U are unnecessary, as you don't use them anywhere, and the class itself is provided by the standard library (The header looks something like data class Pair<out A, out B>(val first: A, val second: B).
If you're determined to use your own Pair class, be sure to make it a data class, so it can be destructured, and change the type of productAndQuantityList to mutableListOf<Pair> (without the type parameters Pair<Product, Int>).
Update
Please read the answer by Mathias Henze, which is correct. My answer, was originally completely wrong, but I have now corrected it.
The productAndQuantityList is just for storing the data. The Pair class is a provided class by Kotlin. You don't need to add anything to it in your usecase.
The ability to print the product and the quantity should be a function of the ShoppingCart, so just have:
class ShoppingCart{
private val productAndQuantityList = mutableListOf<Pair<Product,Int> >()
// ...
fun printContents() {
for ( (product,quantity) in productAndQuantityList) {
println("Name: ${product.productName}")
println("Quantity: $quantity")
}
}
}

is it possible to add a template to the getter/setter of a data class?

for example , I want to change all setters this way:
this.a = StringUtils.trim(a);
If it's a java bean, I can do this by modifying the code generating template of the ide. But Intellij seems not support to atomically add getter/setter for kotlin data class.
Is there a way to do this?
There is not a way to do this as of Kotlin 1.1.
A Kotlin data class, for the most part, is a class "to do nothing but hold data".
I think the closest you can get is to validate your data upon class initialization and make your data class properties read-only values. e.g.:
data class Data(val a: String) {
init {
require(a == a.trim())
}
}
The following won't throw an exception:
val a = Data("ab")
val b = a.copy(a = "abc")
While the following will:
val c = a.copy(a = "abc ")
It looks like if you declare the property as private, you can create your own getter/setters for accessing it. This example works for me.
fun main(args: Array<String>) {
var t = test("foo")
t.setHello("bar")
println(t)
}
data class test(private var hello: String) {
fun setHello(blah: String) {
this.hello = blah
}
}
But you will still have an issue when the property is passed in to the constructor. You will probably need to rethink how you are doing this, either declaring the field private and trimming it in the getter, or not using a data class for this instance.

Jackson deserialization - Kotlin data classes - Defaults for missing fields per mapper

Given this data class:
data class MyPojo(val notInJson: Int, val inJson: Int)
Assume I want to implement a function of the form:
fun deserialize(jsonString: String, valueForFieldNotInJson: Int): MyPojo
Where jsonString does not include a field named notInJson. Assume also, that I have no control over MyPojo class definition.
How could I use Jackson library to deserialize MyPojo from jsonString and augment the missing field (notInJson) from valueForFieldNotInJson parameter?
Notes:
Basically, the question is about deserializing a Immutable class, where some fields come from Json and others are supplied at runtime.
Using custom deserializers or builders will not work because missing values are unknow at compile time.
This can be achieved by combining MinInAnnotations and ValueInjection.
Complete solution as follows:
data class MyPojo(val notInJson: Int, val inJson: Int)
class MyPojoMixIn {
#JacksonInject("notInJson") val notInJson: Int = 0
}
fun deserialize(jsonString: String, valueForFieldNotInJson: Int): MyPojo {
val injectables = InjectableValues.Std().addValue("notInJson", valueForFieldNotInJson)
val reader = jacksonObjectMapper()
.addMixIn(MyPojo::class.java, MyPojoMixIn::class.java)
.readerFor(MyPojo::class.java)
.with(injectables)
return reader.readValue(jsonString)
}