Reputation: 25
hey guys I'm trying to make a database with room and I have a data class as Entity Like this:
@Entity(tableName = "forecast")
data class WeatherForecastEntity(
@PrimaryKey(autoGenerate = false)
val id:Int?,
val city:String?,
val country:String?,
val timeZone:Int?,
val sunrise: Int?,
val sunset: Int?,
val detailList:List<Detail>?
)
as you can see I have a value called detailList that is a List of a data class called Detail that is like this:
data class Detail(
val clouds: Clouds?,
val dt: Int?,
val dt_txt: String?,
val main: Main?,
val pop: Int?,
val sys: Sys?,
val visibility: Int?,
val weather: List<Weather>?,
val wind: Wind?
)
and in this class I have Instance of some other data classes like for example weather value that is a list of Weather data class that is like this:
data class Weather(
val description: String?,
val icon: String?,
val id: Int?,
val main: String?
)
or main that is instance of another data class called Main that is like this:
data class Main(
val feels_like: Double?,
val grnd_level: Int?,
val humidity: Int?,
val pressure: Int?,
val sea_level: Int?,
val temp: Double?
)
and when I run my app I recieve an error that says I must create a type converter and I really have no idea how should I do that with these many data classes that have instance of each other. I'll be really appreciate it if you can help me with it. by the way I'm using RxJava and Gson in my app.
Upvotes: 2
Views: 1302
Reputation: 56938
First. You cannot (I believe) have a val detailList:List<Detail>?
, you need a single (not a List) item.
So (1) add a new Class that embodies the List e.g. :-
data class DetailList(
val detailList: List<Detail>
)
and (2) in WeatherForecastEntity use :-
/* val detailList:List<Detail>? */
val detailList: DetailList?
So now you have a single item/variable/field that needs to be converted by a TypeConverter.
then (3) create a class such as (see note re placement/scope) :-
class DetailListTypeConverter {
@TypeConverter
fun toDetailList(value: String): DetailList {
Log.d("DBINFO_FROMJSON","Extracted>>${value}") /* just for demonstration */
return Gson().fromJson(value,DetailList::class.java)
}
@TypeConverter
fun fromDetailList(value: DetailList): String {
return Gson().toJson(value)
}
}
fromDetailList
function converts a DetailList
(a List<Detail>
) to a String
(a json string of the DetailList object, which embodies all the underlying objects such as Clouds, Sys, Main within the string). This is used when inserting into the database.toDetailList
function does the reverse and builds the DetailList
object (and the underlying objects) from the stored String
. This is used when extracting the data from the database.Then (4) add the following to the @Database
class after the @Database
annotation :-
@TypeConverters(DetailListTypeConverter::class)
Demo
For Example (as used to demo) :-
@Database(entities = [WeatherForecastEntity::class],version = 1)
@TypeConverters(DetailListTypeConverter::class) /*<<<<<<<<<< ADDED >>>>>>>>>>*/
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
companion object {
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase::class.java,
"weather.db"
)
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
.allowMainThreadQueries()
has been used, so the demo runs on the main thread (not recommended for an App that will be distributed)Now using your classes (Classes Sys, Clouds and Wind were created as they aren't included in the question, so they will very likely NOT reflect your code) and the following @Dao
class AllDao (to allow insertion and extraction in the demo):-
@Dao
abstract class AllDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(weatherForecastEntity: WeatherForecastEntity): Long
@Query("SELECT * FROM forecast")
abstract fun getAllForecasts(): List<WeatherForecastEntity>
}
Then using the following code in an activity to demonstrate that the above works :-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
dao.insert(createSomeData())
for(wf: WeatherForecastEntity in dao.getAllForecasts()) {
Log.d("DBINFO","City is ${wf.city}")
for (dl: Detail in wf.detailList!!.detailList) {
Log.d("DBINFO_DETAIL","Detail is ${dl.dt} Clouds is ${dl.clouds!!.name}")
}
}
}
fun createSomeData(): WeatherForecastEntity {
val dtlList: ArrayList<Detail> = arrayListOf()
val dtl1 = Detail(
clouds = Clouds(name = "Cirrus", value = 10.2),
dt = 10,
dt_txt = "The dt for dt1",
main = Main(feels_like = 1.1,grnd_level = 15, humidity = 65,sea_level = 100,pressure = 14,temp = 30.24),pop = 23000,visibility = 300,
weather = listOf(Weather(description = "wet","weticon",0,"wet")),
sys = Sys("sysname",10.333),
wind = Wind(22.5,10.23)
)
val dtl2 = dtl1
dtlList.add(dtl1)
dtlList.add(dtl2)
val detailList = DetailList(dtlList)
return WeatherForecastEntity(null,"London","England",0,100,100,detailList)
}
}
So after 3 runs the log includes :-
2021-09-18 08:19:33.825 D/DBINFO_FROMJSON: Extracted>>{"detailList":[{"clouds":{"name":"Cirrus","value":10.2},"dt":10,"dt_txt":"The dt for dt1","main":{"feels_like":1.1,"grnd_level":15,"humidity":65,"pressure":14,"sea_level":100,"temp":30.24},"pop":23000,"sys":{"name":"sysname","value":10.333},"visibility":300,"weather":[{"description":"wet","icon":"weticon","id":0,"main":"wet"}],"wind":{"direction":22.5,"speed":10.23}},{"clouds":{"name":"Cirrus","value":10.2},"dt":10,"dt_txt":"The dt for dt1","main":{"feels_like":1.1,"grnd_level":15,"humidity":65,"pressure":14,"sea_level":100,"temp":30.24},"pop":23000,"sys":{"name":"sysname","value":10.333},"visibility":300,"weather":[{"description":"wet","icon":"weticon","id":0,"main":"wet"}],"wind":{"direction":22.5,"speed":10.23}}]}
2021-09-18 08:19:33.858 I/chatty: uid=10200(a.a.so69210605kotlinroomgsontypeconverter) identical 4 lines
2021-09-18 08:19:33.864 D/DBINFO_FROMJSON: Extracted>>{"detailList":[{"clouds":{"name":"Cirrus","value":10.2},"dt":10,"dt_txt":"The dt for dt1","main":{"feels_like":1.1,"grnd_level":15,"humidity":65,"pressure":14,"sea_level":100,"temp":30.24},"pop":23000,"sys":{"name":"sysname","value":10.333},"visibility":300,"weather":[{"description":"wet","icon":"weticon","id":0,"main":"wet"}],"wind":{"direction":22.5,"speed":10.23}},{"clouds":{"name":"Cirrus","value":10.2},"dt":10,"dt_txt":"The dt for dt1","main":{"feels_like":1.1,"grnd_level":15,"humidity":65,"pressure":14,"sea_level":100,"temp":30.24},"pop":23000,"sys":{"name":"sysname","value":10.333},"visibility":300,"weather":[{"description":"wet","icon":"weticon","id":0,"main":"wet"}],"wind":{"direction":22.5,"speed":10.23}}]}
2021-09-18 08:19:33.871 D/DBINFO: City is London
2021-09-18 08:19:33.871 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.871 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.871 D/DBINFO: City is London
2021-09-18 08:19:33.871 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.871 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.872 D/DBINFO: City is London
2021-09-18 08:19:33.872 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.872 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
detailList
includes the detail (2 per row) (albeit identical data).The database itself, as per Android Studio's App Inspector :-
Upvotes: 4