How to get two data from Hashmap in room database? - kotlin

I took the data from a money exchange API and saved it to the room. I want to extract 2 values ​​from the data I saved, for example, the user wants to convert 120 dollars to euros. I will take the conversion rate of the dollar and the conversion rate of the euro from my room database and convert it with a mathematical operation accordingly. However, I did not know how to get these two values ​​from my data that I keep as Hashmap.
I wrote a code like this,
dao
#Dao
interface ExchangeDao {
#Query("SELECT * FROM ExchangeValues WHERE conversion_rates=:currency")
suspend fun getConversionRateByCurrency(currency : String) : Double
}
entity
#Entity(tableName = "ExchangeValues")
data class ExchangeEntity(
#ColumnInfo(name = "base_code") val base_code: String,
#ColumnInfo(name = "conversion_rates") val conversion_rates: HashMap<String,Double>,
#ColumnInfo(name = "result") val result: String,
#PrimaryKey(autoGenerate = true) val uid:Int?=null
)
repositoryImpl
class ExchangeRepositoryImpl #Inject constructor(
private val dao:ExchangeDao,
private val api: ExchangeApi
) : ExchangeRepository{
override suspend fun getConversionRateByCurrency(currency: String): Double {
return dao.getConversionRateByCurrency(currency)
}
}
repository
interface ExchangeRepository {
suspend fun getConversionRateByCurrency(currency : String) : Double
}
use case
class GetConversionRateByCurrencyUseCase #Inject constructor(
private val repository: ExchangeRepository
) {
suspend fun getConversionRateByCurrency(currency:String) : Double {
return repository.getConversionRateByCurrency(currency)
}
}
but it gave an error like this
error: Not sure how to convert a Cursor to this method's return type (java.lang.Double).
public abstract java.lang.Object getConversionRateByCurrency(#org.jetbrains.annotations.NotNull()
error: The columns returned by the query does not have the fields [value] in java.lang.Double even though they are annotated as non-null or primitive. Columns returned by the query: [base_code,conversion_rates,result,uid]
public abstract java.lang.Object getConversionRateByCurrency(#org.jetbrains.annotations.NotNull()

Related

Access Data class property while using Generic Type parameter <T> in runtime [Kotlin]

I'm trying to return a property/value from Data class by checking type parameter
Data Class :
data class SystemConfiguration(
val systemName: String,
val fields: List<String>
)
Abstract class :
abstract class ConfigurationLoader<out T> {
abstract val clazz: Class<out T>
private fun getAssociateAttribute(file: T): String{
return when(clazz){
SystemConfiguration::class.java -> file.systemName // Line causing error
// If its another Data class, I should return value from that data class
else -> ""
}
}
open fun loadConfigurations(): Map<String, T>{
val map = Paths.get(configurationFolderPath, filePath).toFile().walkTopDown().filter { it.isFile }.map {
val x = ionSystem.loader.load(it)[0]
ionValueMapper.readValue<List<T>>(x.toString())
}.flatten().associateBy { getAssociateAttribute(it) }
}
}
inline fun <reified T: Any> javaClasstype(): Class<T> {
return T::class.java
}
Sub class
class ServiceConfigurationLoader (
override val clazz: Class<SystemConfiguration> = javaClasstype()
): ConfigurationLoader<SystemConfiguration>()
I'm getting an exception "e: Unresolved reference: systemName". Not able to access values inside data class while we use type parameter
If i use like this(directly mentioning data class name), I'm able to access the values
private fun getAssociateAttribute(file: SystemConfiguration): String{
return when(clazz){
SystemConfiguration::class.java -> file.systemName
else -> ""
}
}
Could someone help me out here to access the value using Type paramater in Kotlin?
Thanks in Advance !!
I have tried using reified keyword as well. Still getting the same issue. I'm expecting to access the value using Type paramater

How to save numbers in room database kotlin

I want to create a simple financial report app.
With opening balance, inventory purchase, other expenses, sales and banking all as double numbers I want to use room database, jetpack components
You can't ask this type of questions but look at this sample (simplest way to use roomDB)
First of all you need to define you database class
#Database(entities = [CompaniesModel::class, UserPoint::class], version = 15) //here is the models which will have the structure of your database
abstract class DataBase : RoomDatabase() {
/**
* define companies dao to make some quires
*/
abstract fun homeDao(): HomeDao
abstract fun companiesDao(): CompaniesListDao
companion object {
#Volatile
private var databaseInstance: DataBase? = null
fun getDatabaseInstance(mContext: Context): DataBase =
databaseInstance ?: synchronized(this) {
databaseInstance ?: buildDatabaseInstance(mContext).also {
databaseInstance = it
}
}
private fun buildDatabaseInstance(mContext: Context) =
Room.databaseBuilder(mContext, DataBase::class.java, "crm")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build()
}
}
and the models which contain the structure of your database
#Entity(tableName = "companiesModel")
data class CompaniesModel(
#PrimaryKey
#ColumnInfo(name = "id")
#SerializedName("id")
var id: Int,
#ColumnInfo(name = "name")
#SerializedName("name")
var name: String,
#ColumnInfo(name = "image")
#SerializedName("image")
var image: String
)
and the Dao which have all your queres
#Dao
interface CompaniesListDao {
/**
* this fun to insert data in room db after fetch data from server
*/
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCompanies(contactModel: List<CompaniesModel>)
/**
* this fun to get data from room db to make some caching in app
*/
#Query("SELECT * FROM companiesModel")
fun getCompaniesList(): List<CompaniesModel>
/**
* this fun to clear companies list from room db
*/
#Query("DELETE FROM companiesModel")
fun clearCompaniesList()
#Update
fun update(contactModel: List<CompaniesModel>)
}
to access the data you should call it like this
DataBase.getDatabaseInstance(App.instance).companiesDao().something

How do i serialize a generic sealed class with kotlinx.serialization

Not sure if it is possible yet but for the life of me I cannot figure out how to serialize this.
sealed class ServiceResult<out T : Any> {
data class Success<out T : Any>(val data: T) : ServiceResult<T>()
data class Error(val exception: Exception) : ServiceResult<Nothing>()
}
Everything that is stuff into T is using #Serializable ex:
#Serializable
data class GalleryDTO(
override val id: Int,
override val dateCreated: Long,
override val dateUpdated: Long,
val name:String,
val description:String,
val photos:List<DTOMin>
) : DTO
As Animesh Sahu already mentioned there is an issue for this topic that is still open, but the solution using a surrogate suggested by Михаил Нафталь for serialization of Error can actually be used also to serialize the polymorphic ServiceResult, by creating a surrogate that mixes the fields of Success and Error. For the sake of simplicity in the example I only represent the exception message.
#Serializable(with = ServiceResultSerializer::class)
sealed class ServiceResult<out T : Any> {
data class Success<out T : Any>(val data: T) : ServiceResult<T>()
data class Error(val exceptionMessage: String?) : ServiceResult<Nothing>()
}
class ServiceResultSerializer<T : Any>(
tSerializer: KSerializer<T>
) : KSerializer<ServiceResult<T>> {
#Serializable
#SerialName("ServiceResult")
data class ServiceResultSurrogate<T : Any>(
val type: Type,
// The annotation is not necessary, but it avoids serializing "data = null"
// for "Error" results.
#EncodeDefault(EncodeDefault.Mode.NEVER)
val data: T? = null,
#EncodeDefault(EncodeDefault.Mode.NEVER)
val exceptionMessage: String? = null
) {
enum class Type { SUCCESS, ERROR }
}
private val surrogateSerializer = ServiceResultSurrogate.serializer(tSerializer)
override val descriptor: SerialDescriptor = surrogateSerializer.descriptor
override fun deserialize(decoder: Decoder): ServiceResult<T> {
val surrogate = surrogateSerializer.deserialize(decoder)
return when (surrogate.type) {
ServiceResultSurrogate.Type.SUCCESS ->
if (surrogate.data != null)
ServiceResult.Success(surrogate.data)
else
throw SerializationException("Missing data for successful result")
ServiceResultSurrogate.Type.ERROR ->
ServiceResult.Error(surrogate.exceptionMessage)
}
}
override fun serialize(encoder: Encoder, value: ServiceResult<T>) {
val surrogate = when (value) {
is ServiceResult.Error -> ServiceResultSurrogate(
ServiceResultSurrogate.Type.ERROR,
exceptionMessage = value.exceptionMessage
)
is ServiceResult.Success -> ServiceResultSurrogate(
ServiceResultSurrogate.Type.SUCCESS,
data = value.data
)
}
surrogateSerializer.serialize(encoder, surrogate)
}
}
This solution can also be easily extended to support nullable Ts. In this case when deserializing you will also have to check if null is a valid value for T (it can be done by checking descriptor.isNullable on tSerializer) and you will also have to cast data as T.
Polymorphic serialization will be a mess in this case (you will have to manually register all possible types passed as a generic parameter to ServiceResult<T>), and will have several limitations (it would be impossible to register primitive types (including Nothing and String) as generic parameters, for instance).
If you only need serialization (aka encoding), I'd recommend to serialize both subtypes independently (for convenience, wrap subtype determination into auxilary function):
inline fun <reified T : Any> serializeServiceResult(x: ServiceResult<T>) = when (x) {
is ServiceResult.Success -> Json.encodeToString(x)
is ServiceResult.Error -> Json.encodeToString(x)
}
To serialize ServiceResult.Success you need just to mark it with #Serializable annotation. The tricky part here is serialization of ServiceResult.Error, or more precisely, serialization of its exception: Exception field. I'd suggest to serialize only its message (via surrogate):
sealed class ServiceResult<out T : Any> {
#Serializable
data class Success<out T : Any>(val data: T) : ServiceResult<T>()
#Serializable(with = ErrorSerializer::class)
data class Error(val exception: Exception) : ServiceResult<Nothing>()
}
#Serializable
private data class ErrorSurrogate(val error: String)
class ErrorSerializer : KSerializer<ServiceResult.Error> {
override val descriptor: SerialDescriptor = ErrorSurrogate.serializer().descriptor
override fun deserialize(decoder: Decoder): ServiceResult.Error {
val surrogate = decoder.decodeSerializableValue(ErrorSurrogate.serializer())
return ServiceResult.Error(Exception(surrogate.error))
}
override fun serialize(encoder: Encoder, value: ServiceResult.Error) {
val surrogate = ErrorSurrogate(value.exception.toString())
encoder.encodeSerializableValue(ErrorSurrogate.serializer(), surrogate)
}
}

Room query with id doesn't return the right list RxJava MVVM architecture

I have a problem concerning my query returns, I have a student class that contains a string id from another table
data class StudentEntity(
#PrimaryKey
val idStudent: String,
val classId: String,
val name: String,
val notes: Note?,
)
I also created a room database that I'm populating from my api call
#Database(entities = [Student::class, Note::class], version = 14, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun programDAO(): ProgramDAO
companion object{
#Volatile
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context) : AppDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext, AppDatabase::class.java, "student-database"
).fallbackToDestructiveMigration().build()
INSTANCE = instance
}
return instance
}
}
}
}
and for that, I have a programDao that helps me to run my queries
#Dao
interface ProgramDAO {
#Transaction
#Query("select * from studentEntity")
fun getStudents(): Single<List<StudentEntity>>
#Transaction
#Query("select * from studentEntity where classId = :classid")
fun getStudentsWithId(classid: String): Single<List<StudentEntity>>
}
In order to execute those queries, I have my Repository:
class ProgramRepository(val api: ApiService, val programDAO: ProgramDAO) {
fun getStudentsFromDbWithId(idClass: String) : Observable<StudentEntity>{
return programDAO.getStudentsWithId(idClass).toObservable()
}
fun getStudentsFromDb() : Observable<StudentEntity>{
return programDAO.getStudents().toObservable()
}
}
The MV allows me to connect the data and the view:
class ProgramListViewModel(private val programRepository: ProgramRepository) {
fun getListFromDBWithId(classID: String): Observable<List<StudentEntity>> {
return programRepository.getStudentsFromDbWithId(deliverySiteId)
}
fun getListFromDB(): Observable<List<StudentEntity>> {
return programRepository.getStudentsFromDb()
}
}
So in order to use the data and get the adapter and the KPIs on my fragment, I don't receive the right list from the database, I did log the results to see what I get, to start, I do log the whole list without id, and I have the list, but when I use an ID like:
subscribe(programListViewModel.getListFromDBWithId("111").subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.e("list of students", it.toString())
})
I got an empty list, so I thought the problem was from the id, I tried to use the id that has been logged from the whole list and didn't work, I also used S to launch my sql query with the same id and I got the results.
Any help please?
Thanks
Is your AircraftEntity supposed to be StudentEntity? If yes (and I think it is, according to the rest of your code examples), then please update your question.
By default, Room uses the class name as the database table name, and I think it's case-sensitive, so your queries should have been "select * from StudentEntity". The better approach would be giving it a name explicitly:
#Entity(tableName = "students")
data class StudentEntity (
// ...
)
Then, your ProgramDAO would look like follow:
#Dao
interface ProgramDAO {
#Query("select * from students")
fun getStudents(): Single<List<StudentEntity>>
#Query("select * from students where classId = :classid")
fun getStudentsWithId(classid: String): Single<List<StudentEntity>>
}
You said: ... a room database that I'm populating from my api call
Where and how do you populate your database? I don't see any #Insert functions in your DAO, unless you just left them out from your code snippet. If your room DB is not populated, of course you will get no data.

Access properties of a subclass of the declared object type

I have the following abstract class:
abstract class AbstractBook {
abstract val type: String
abstract val privateData: Any
abstract val publicData: Any
}
and the following class which inherits the AbstactBook class:
data class FantasyBook (
override val type: String = "FANTASY",
override val privateData: FantasyBookPrivateData,
override val publicData: FantasyBookPublicData
) : AbstractBook()
And then there is this class which should include data from any type of AbstractBook:
data class BookState(
val owner: String,
val bookData: AbstractBook,
val status: String
)
If I have an instance of BookState, how do I check which type of Book it is and then access the according FantasyBookPrivateData, and FantasyBookPublicData variables?
I hope I described my issue well & thanks in advance for any help!
What you describe is a sealed class:
sealed class Book<T, K> {
abstract val type: String
abstract val privateData: T
abstract val publicData: K
data class FantasyBook(
override val type: String = "FANTASY",
override val privateData: String,
override val publicData: Int) : Book<String, Int>()
}
and in your data class you can do pattern matching like this:
data class BookState(
val owner: String,
val bookData: Book<out Any, out Any>,
val status: String) {
init {
when(bookData) {
is Book.FantasyBook -> {
val privateData: String = bookData.privateData
}
}
}
}
to access your data in a type-safe manner. This solution also makes type redundant since you have that information in the class itself.
I agree with #Marko Topolnik that this seems like a code smell, so you might want to rethink your design.
interface AbstractBook<T , U> {
val privateData: T
val publicData: U
}
data class FantasyBook (
override val privateData: FantasyBookPrivateData,
override val publicData: FantasyBookPublicData
) : AbstractBook<FantasyBookPrivateData , FantasyBookPublicData>
data class BookState(
val owner: String,
val bookData: AbstractBook<*, *>,
val status: String
)
if(bookState.bookData is FantasyBook) {
// Do stuff
}
Creating a type variable is a weak type language writing style. You should use generic class.