How to make a converter class correctly? - android

I have POJO data classes from API.
#Entity(tableName = "pokemon")
class Pokemon(
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
#SerializedName("count")
#Expose
val count: Int?,
#SerializedName("next")
#Expose
val next: String?,
#SerializedName("results")
#Expose
val results: List<Result>? = null
)
And class List:
class Result(
#SerializedName("name")
#Expose
val name: String?,
#SerializedName("url")
#Expose
val url: String?
)
My database class:
#Database(entities = [Pokemon::class], version = 1, exportSchema = false)
#TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
companion object {
private var db: AppDatabase? = null
private const val DB_NAME = "main.db"
private val LOCK = Any()
fun getInstance(context: Context): AppDatabase {
synchronized(LOCK) {
db?.let { return it }
val instance = Room.databaseBuilder(
context,
AppDatabase::class.java,
DB_NAME
)
.fallbackToDestructiveMigration()
.build()
db = instance
return instance
}
}
}
abstract fun PokemonDao(): PokemonDao
}
My class Converters:
class Converters {
#TypeConverters
fun fromResultToString(result: slode.elsloude.pokemonapi.pojo.Result?): String? {
return Gson().toJson(result)
}
#TypeConverters
fun fromStringToResult(value: String?): slode.elsloude.pokemonapi.pojo.Result? {
return Gson().fromJson(value, Result::class.java)
}
}
The error I get when attempting this is:
error: Class is referenced as a converter but it does not have any converter methods. - androidx.databinding.adapters.ConvertersC:\Users\elslode\.AndroidStudio3.6\PokemonApi\app\build\tmp\kapt3\stubs\debug\slode\elsloude\pokemonapi\pojo\Pokemon.java:22: error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
private final java.util.List<slode.elsloude.pokemonapi.pojo.Result> results = null;
^C:\Users\elslode\.AndroidStudio3.6\PokemonApi\app\build\tmp\kapt3\stubs\debug\slode\elsloude\pokemonapi\pojo\SinglePokemon.java:14: error: Cannot figure out how to read this field from a cursor.

The #TypeConverters annotation is used to link your converters with the database, as yuo have done here:
#TypeConverters(Converters::class)
However when declaring a converter method you should be using the #TypeConverter annotation:
class Converters {
#TypeConverter
fun fromResultToString(result: slode.elsloude.pokemonapi.pojo.Result?): String? {
return Gson().toJson(result)
}
#TypeConverter
fun fromStringToResult(value: String?): slode.elsloude.pokemonapi.pojo.Result? {
return Gson().fromJson(value, Result::class.java)
}
}
Note the plural on #TypeConverters vs #TypeConverter.
Secondly Room cannot automatically infer a list conversion for you, so the type converter functions will have to map the full List<Result> to a String and vice versa.

Related

Android Room - ColumnInfo Cannot find getter for field. private final ... = null;

error: Cannot find getter for field.
private final com.kbb.webviewolacakmi.model.content icerik = null;
I didn't manage to add the subparts of the json to the room.
Thanks to everyone who helped.
I would be very happy if you could write a clear code example.
Json File :
{
"date": "xxx",
"title": {
"rendered": "Title"
},
"content": {
"rendered": "content",
"protected": false
},
}
Data Class :
#Entity
data class Icerik(
#ColumnInfo(name="title")
#SerializedName("title")
val baslik:title?,
#ColumnInfo(name="content")
#SerializedName("content")
public val icerik:content?,
#ColumnInfo(name="date")
#SerializedName("date")
val tarih:String?,
#ColumnInfo(name="jetpack_featured_media_url")
#SerializedName("jetpack_featured_media_url")
val gorsel:String?,) {
#PrimaryKey(autoGenerate = true)
var uuid:Int=0
fun getIcerik(){
}
}
data class content(
#ColumnInfo(name="rendered")
#SerializedName("rendered")
public val content: String?,
#ColumnInfo(name="protected")
#SerializedName("protected")
val bool: Boolean?,
){
#PrimaryKey(autoGenerate = true)
var uuid:Int=0
}
data class title(
#ColumnInfo(name="rendered")
#SerializedName("rendered")
val ytitle:String?
){
#PrimaryKey(autoGenerate = true)
var uuid:Int=0
}
IcerikDatabase Class
#TypeConverters(value = [RoomTypeConverters::class])
#Database(entities = arrayOf(Icerik::class), version = 1)
abstract class IcerikDatabase:RoomDatabase() {
abstract fun icerikDao(): IcerikDAO
companion object {
#Volatile private var instance:IcerikDatabase? = null
private val lock=Any()
operator fun invoke(context: Context)= instance?: synchronized(lock){
instance?: databaseOlustur(context).also {
instance=it
}
}
private fun databaseOlustur(context: Context) = Room.databaseBuilder(
context.applicationContext, IcerikDatabase::class.java,
"icerikdatabase"
).build()
}
}
IcerikDao
interface IcerikDAO {
#Insert
suspend fun instertAll(vararg icerik:Icerik):List<Long>
#Query("SELECT * FROM icerik")
suspend fun getAllIcerik():List<Icerik>
#Query("SELECT * FROM icerik WHERE uuid=:icerikId ")
suspend fun getIcerik(icerikId:Int):Icerik
#Query("DELETE FROM icerik")
suspend fun deleteAllIcerik()
}
TypeConverter
class RoomTypeConverters {
#TypeConverter
fun fromTitleToJSONString(title: title?): String? {
return Gson().toJson(title)
}
#TypeConverter
fun toTitleFromJSONString(jsonString: String?): title? {
return Gson().fromJson(jsonString, title::class.java)
}
#TypeConverter
fun fromIcerikToJSONString(content: content?): String? {
return Gson().toJson(content)
}
#TypeConverter
fun toIcrerikFromJSONString(jsonString: String?): content? {
return Gson().fromJson(jsonString, content::class.java)
}
}
I believe that your issue is in regard, not to room, but with the JSON handling.
The JSON file that you have shown cannot directly build an Icerik object.
Rather you need to have an intermediate class that can be built with the JSON and then use that intermediate class to then build the IceRik object.
So you want an intermediate class something along the lines of:-
data class JsonIceRik(
val content: content,
val title: title,
val date: String
)
If the JSON is then amended to be:-
val myjson = "{\"date\": \"xxx\",\"title\": {\"rendered\": \"Title\"},\"content\": {\"rendered\": \"content\",\"protected\": false}}"
note the omission of the comma between the two closing braces
Then you could use:-
val m5 = Gson().fromJson(myjson,JsonIceRik::class.java)
To build the intermediate JsonIceRik object.
And then you could use:-
val i5 = Icerik(baslik = m5.title, icerik = m5.content, tarih = m5.date,gorsel = "whatever")
To build the Icerik from the intermediate JsonIceRik.
The result in the database would be:-
uuid in the title and content serve no purpose and will always be 0 if obtaining the data from JSON
the #PrimaryKey annotation only serves to introduce warnings -
A Table can only have 1 Primary Key ( a composite Primary Key can include multiple columns though (but you cannot use the #PrimaryKey annotation, you have to instead use the primarykeys parameter of the #Entity annotation) )
You might as well have :-
data class content(
#ColumnInfo(name="rendered")
#SerializedName("rendered")
val content: String?,
#ColumnInfo(name="protected")
#SerializedName("protected")
val bool: Boolean?,
)
data class title(
#ColumnInfo(name="rendered")
#SerializedName("rendered")
val ytitle:String?
)
Otherwise, as you can see, the data according to the JSON has been correctly stored.

Error of type convertors in room database

I am trying to make a type convertor for a model class in my app. The model class is given below.
Order
#Entity(tableName = "Orders")
data class Order(
#PrimaryKey
var customerId:String,
var companyId:String,
var orderBookerId:String,
#ColumnInfo(name = "items") var list:List<SelectedProductsModel>
)
SelectedProductsModel
data class SelectedProductsModel(
var quantity: Int,
var skuId: String,
var companyId: String,
var productName: String?,
var vendor_brand_name: String,
var purchasePrice: Int,
var image: String,
var position: Int
):Serializable
now as you can see that in the Order class there is a var list:List<SelectedProductsModel>. I want to write a type converter for this list but i keep getting an error
error: cannot find symbol
#androidx.room.TypeConverters(value = {SelectedProductModelConvertor.class})
following is my Room database class
#Database(entities = [VisitedPjpModel::class,Order::class], version = 1)
#TypeConverters(SelectedProductModelConvertor::class)
abstract class RoomDatabaseClass : RoomDatabase()
{
public abstract fun visitedDao(): VisitedPjpDao
public abstract fun orderDao():OrderDao
companion object
{
private var instance: RoomDatabaseClass? = null
fun getInstance(context: Context): RoomDatabaseClass?
{
if (instance == null)
{
synchronized(RoomDatabaseClass::class)
{
instance = Room.databaseBuilder(context, RoomDatabaseClass::class.java, "tarseel_database").allowMainThreadQueries().build()
}
}
return instance
}
}
}
i have tried different implementations of type convertors but iam getting the same error which is mentioned above. following are the implementations of type converotrs that i have tried
class SelectedProductModelConvertor
{
#TypeConverter
fun fromList(value : List<SelectedProductsModel>) = Json.encodeToString(value)
#TypeConverter
fun toList(value: String) = Json.decodeFromString<List<SelectedProductsModel>>(value)
}
class SelectedProductModelConvertor
{
#TypeConverter
fun listToJson(value: List<SelectedProductsModel>) = Gson().toJson(value)
#TypeConverter
fun jsonToList(value: String) = Gson().fromJson(value, Array<SelectedProductsModel>::class.java).toList()
}
class SelectedProductModelConvertor
{
#TypeConverter
fun fromListLangList(value: List<SelectedProductsModel>): String
{
val gson = Gson()
val type = object : TypeToken<List<SelectedProductsModel>>()
{}.type
return gson.toJson(value, type)
}
#TypeConverter
fun toSelectedProductsModelList(value: String): List<SelectedProductsModel>
{
val gson = Gson()
val type = object : TypeToken<List<SelectedProductsModel>>()
{}.type
return gson.fromJson(value, type)
}
}
I have tried all these type convertors but iam getting error. please help me
You cannot have a List/Array for a column.
What you can do, is have a class that holds the List/Array and have a Type Converter for that.
So you could have:-
data class SelectedProductsModelList(
val selectedProductsModelList: List<SelectedProductsModel>
)
Then have :-
#Entity(tableName = "Orders")
data class Order(
#PrimaryKey
var customerId:String,
var companyId:String,
var orderBookerId:String,
#ColumnInfo(name = "items") var list:SelectedProductsModelList //<<<<<<<<<<
)
and TypeConverters that convert from/to a SelectProductsModelList object e.g.
#TypeConverter
fun fromSelectedProductsModelList(selectedProductsModelList: SelectedProductsModelList): String {
return Gson().toJson(selectedProductsModelList)
}
#TypeConverter
fun toSelectedProductsModelList(json: String): SelectedProductsModelList {
return Gson().fromJson(json,SelectedProductsModelList::class.java)
}

Android room/TypeConverter issue while retriving list of objects

I am implementing a local cache using Room. I have created typeconverter to convert list of objects to json and back. But I am receiving mapping issue while retrieving data from json with error:
The columns returned by the query does not have the fields [title,media] in
com.example.theApp.data.FlickrImage even though they are annotated as non-null or
primitive. Columns returned by the query: [items]
Another one like this:
error: Cannot figure out how to read this field from a cursor.
private final com.example.theApp.data.Media media = null;
I tried other answers here but its not associated directly with this issue.
Here is my typeconverter:
class FlickrImageConverters {
#TypeConverter
fun fromImageListToJson(stat: List<FlickrImage>): String {
return Gson().toJson(stat)
}
/**
* Convert a json to a list of Images
*/
#TypeConverter
fun fromJsonToImagesList(jsonImages: String): List<FlickrImage> {
val type = object : TypeToken<List<FlickrImage>>() {}.type
return Gson().fromJson<List<FlickrImage>>(jsonImages, type)
}
}
Here is my entity class:
#Entity
data class DatabaseImagesEntity(
#PrimaryKey
#TypeConverters(FlickrImageConverters::class)
#SerializedName("item")
val items: List<FlickrImage>)
Dao class
#Dao
interface ImagesDao {
#Query("select * from DatabaseImagesEntity")
fun getImages(): List<FlickrImage>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(images: List<FlickrImage>)
}
FlickrImage class
data class FlickrImage(val title: String, val media: Media)
Media class
data class Media(val m: String)
LatestImage class
data class LatestImages(val items: List<FlickrImage>)
Please let me know if you faced this issue and if you know the solution for this.
Room database implementation
#Database(entities = [DatabaseImagesEntity::class], version = 1,
exportSchema = false)
#TypeConverters(FlickrImageConverters::class)
abstract class FlickrDatabase: RoomDatabase() {
abstract val imagesDao: ImagesDao
}
private lateinit var INSTANCE: FlickrDatabase
fun getDatabase(context: Context): FlickrDatabase{
synchronized(FlickrDatabase::class.java){
if(!::INSTANCE.isInitialized){
INSTANCE = Room.databaseBuilder(context.applicationContext,
FlickrDatabase::class.java,
"flickerImages").build()
}
}
return INSTANCE
}
The issue was I was saving data in the wrong entity, wrong TypeConverters and as a result, I was using the wrong Entity class at the time of database creation.
Here are the necessary changes I had to make to store the list of objects:
Flickr data class
#Entity(tableName = "FlickerImage")
data class FlickrImage(
#PrimaryKey(autoGenerate = true)
val id: Int,
val title: String,
#TypeConverters(MediaConverter::class)
val media: Media)
TypeConvertors for Media class
class MediaConverter {
#TypeConverter
fun fromMediaToJson(stat: Media): String {
return Gson().toJson(stat)
}
/**
* Convert a json to a list of Images
*/
#TypeConverter
fun fromJsonToMedia(jsonImages: String): Media {
val type = object : TypeToken<Media>() {}.type
return Gson().fromJson<Media>(jsonImages, type)
}
}
DAO class
#Dao
interface ImagesDao {
#Query("select * from FlickerImage")
fun getImages(): LiveData<List<FlickrImage>>
Database class
#Database(entities = [FlickrImage::class], version = 1, exportSchema = false)
#TypeConverters(MediaConverter::class)
abstract class FlickrDatabase: RoomDatabase() {
abstract val imagesDao: ImagesDao
}
private lateinit var INSTANCE: FlickrDatabase
fun getDatabase(context: Context): FlickrDatabase{
synchronized(FlickrDatabase::class.java){
if(!::INSTANCE.isInitialized){
INSTANCE = Room.databaseBuilder(context,
FlickrDatabase::class.java,
"flickerImages").build()
}
}
return INSTANCE
}
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(images: List<FlickrImage>)
}
You need to add the appropriate annotations to your data class - e.g. for Gson you need to add the annotations #SerializedName("field_name") Otherwise, there's no way for the converters to know how to translate the json.
To clarify, the current annotations you have are only for Room. Just check with whatever json library you are using for the necessary logic.
#Entity(tableName = "images")
data class DatabaseImagesEntity(
#PrimaryKey(autoGenerate = true)
var id: Int? = 0,
#TypeConverters(FlickrImageConverters::class)
#SerializedName("item")
val items: MutableList<FlickrImage>? = null
)
or
#Entity(tableName = "images")
class DatabaseImagesEntity {
#PrimaryKey(autoGenerate = true)
var id: Int? = 0
#TypeConverters(FlickrImageConverters::class)
#SerializedName("item")
val items: MutableList<FlickrImage>? = null
}
then update your DAO query to #Query("select * from images")
I named it images as an example - you can choose whatever you want.
class ListConverter {
//from List to String
#TypeConverter
fun fromList(list : List<Object>): String {
return Gson().toJson(list)
}
// from String to List
#TypeConverter
fun toList(data : String) : List<Object> {
if (data == null){
return Collections.emptyList()
}
val typeToken = object : TypeToken<List<Object>>() {}.type
return Gson().fromJson(data,typeToken)
}
}

Room - save custom object list

I'm trying to save List<Object> into Room database.
I get following error:
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
private java.util.List<xxx.models.OBJECTX> carList;
Room implementation:
#Entity(tableName = "nameOfTable")
class CachedObjectX(
#PrimaryKey
#ColumnInfo(name = "id") val id: Long,
#ColumnInfo(name = "list")
var carList: List<ObjectX>
)
#Dao
interface CachedObjectXDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(list: ArrayList<CachedObjectX>)
#Delete
fun delete(list: ArrayList<CachedObjectX>)
#Query("SELECT * FROM ...")
fun getAll(): ArrayList<CachedObjectX>
}
class CachedObjectXConverter {
companion object {
var gson = Gson()
#TypeConverter
#JvmStatic
fun toInsuredVehicle(json: String): List<ObjectX> {
val type = object : TypeToken<List<ObjectX>>() {}.type
return gson.fromJson(json, type)
}
#TypeConverter
#JvmStatic
fun toJson(torrent: List<ObjectX>): String {
val type = object: TypeToken<List<ObjectX>>() {}.type
return gson.toJson(torrent, type)
}
}
}
#Database(entities = [CachedObjectX::class], version = 1, exportSchema = false)
#TypeConverters(CachedObjectXConverter::class)
abstract class CachedObjectXDb : RoomDatabase() {
companion object {
private const val DB_NAME = "CachedObjectX.db"
val instance: CachedObjectXDb by lazy {
Room.databaseBuilder(
getContext(),
CCchedObjectXDb::class.java,
DB_NAME
).build()
}
}
abstract fun getDao(): CachedObjectXDao
}
Interesting, that I've added TypeConverter, but it still throws error. What is wrong with my implementation? Just started with Room, so there is high chance that something is wrong. Thanks in advance.
You have to make the pojo of ObjectX be an entity too

Android room save custom type

I try save custom type in room database. I always getting an error
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
I created converter for OutboxItemCache
My entity
#Entity(tableName = "request")
class RequestCache(
#ColumnInfo(name = "requestId")
#PrimaryKey var id: String = "",
#TypeConverters(RequestConverter::class)
var outboxItemCache: OutboxItemCache)
My RequestConverter
class RequestConverter {
companion object {
private val gson = Gson()
#TypeConverter
fun stringToOutboxItem(string: String): OutboxItemCache? {
if (TextUtils.isEmpty(string))
return null
return gson.fromJson(string, OutboxItemCache::class.java)
}
#TypeConverter
private fun outboxItemToString(outboxItem: OutboxItemCache): String {
return gson.toJson(outboxItem)
}
}
}
This my class OutboxItemCache
class OutboxItemCache(
var id: String = "",
val numder: String,
date: String,
val headerDate: String,
name: String,
val certCount: String,
val status: String,
val statusColor: Int,
var statusInfo: String? = null,
val vetCertIds: List<String>
)
The error never disappeared
As per google's documentation:
#Database(entities = {User.class}, version = 1)
#TypeConverters({RequestConverter.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
You need to register your TypeConverters to your Database class.
EDIT:
In kotlin:
#Database(entities = arrayOf(User::class), version = 1)
#TypeConverters(RequestConverter::class)
abstract class AppDatabse : RoomDatabase() {
abstract fun userDAO(): UserDAO
}
The only line of code here that matters to you is
#TypeConverters(RequestConverter::class)
as you should already have the others.
EDIT 2:
Try writing your converter class as follows:
class RequestConverter {
#TypeConverter
fun stringToOutboxItem(string: String): OutboxItemCache? {
if (TextUtils.isEmpty(string))
return null
return Gson().fromJson(string, OutboxItemCache::class.java)
}
#TypeConverter
private fun outboxItemToString(outboxItem: OutboxItemCache): String {
return Gson().toJson(outboxItem)
}

Categories

Resources