Android room persistent library - TypeConverter error of error: Cannot figure out how to save field to database"
Im not able to create a typeConverter in room due to an error. I seem to be following everything per the docs. I would like to convert a list to a json string. lets take a look at my entity:
@Entity(tableName = TABLE_NAME)
public class CountryModel {
public static final String TABLE_NAME = "Countries";
@PrimaryKey
private int idCountry;
/* I WANT TO CONVERT THIS LIST TO A JSON STRING */
private List<CountryLang> countryLang = null;
public int getIdCountry() {
return idCountry;
}
public void setIdCountry(int idCountry) {
this.idCountry = idCountry;
}
public String getIsoCode() {
return isoCode;
}
public void setIsoCode(String isoCode) {
this.isoCode = isoCode;
}
public List<CountryLang> getCountryLang() {
return countryLang;
}
public void setCountryLang(List<CountryLang> countryLang) {
this.countryLang = countryLang;
}
}
The country_lang is what i would like to convert to a string json. So i created the following converter: Converters.java:
public class Converters {
@TypeConverter
public static String countryLangToJson(List<CountryLang> list) {
if(list == null)
return null;
CountryLang lang = list.get(0);
return list.isEmpty() ? null : new Gson().toJson(lang);
}}
then the problem is anywhere i put the @TypeConverters({Converters.class}) i keep getting an error. But officially this is where i have placed the annotation to get the typeConverter registered:
@Database(entities = {CountryModel.class}, version = 1 ,exportSchema = false)
@TypeConverters({Converters.class})
public abstract class MYDatabase extends RoomDatabase {
public abstract CountriesDao countriesDao();
}
The error i get is:
Error:(58, 31) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
Solution 1:
This is a common problem I've seen since Room was announced. Room does not support the ability to store Lists directly, nor the ability to convert to/from Lists. It supports converting and storing POJO's.
In this case the solution is simple. Instead of storing a List<CountryLang>
you want to store CountryLangs
(note the 's')
I've done a quick example of a solution here :
public class CountryLangs {
private List<String> countryLangs;
public CountryLangs(List<String> countryLangs) {
this.countryLangs = countryLangs;
}
public List<String> getCountryLangs() {
return countryLangs;
}
public void setCountryLangs(List<String> countryLangs) {
this.countryLangs = countryLangs;
}
}
This POJO is an inversion of your previous object. It is an object that stores a list of languages. Instead of a list of objects that store your language.
public class LanguageConverter {
@TypeConverter
public CountryLangs storedStringToLanguages(String value) {
List<String> langs = Arrays.asList(value.split("\\s*,\\s*"));
return new CountryLangs(langs);
}
@TypeConverter
public String languagesToStoredString(CountryLangs cl) {
String value = "";
for (String lang :cl.getCountryLangs())
value += lang + ",";
return value;
}
}
This converter takes a list of strings and converts them into a comma seperated string to be stored in a single column. When it fetches the string from the SQLite db to convert back, it splits the list on commas, and populates the CountryLangs.
Insure to update your RoomDatabase version after making these changes.You have the rest of the configuration correct. Happy hunting with the rest of your Room persistence work.
Solution 2:
Had same error: "Cannot figure out how to save this field into database" while trying to add a Date field. Had to add a converter class for it and add @TypeConverters annotation to field.
Example:
WordEntity.java
import androidx.room.TypeConverters;
@Entity
public class WordEntity {
@PrimaryKey(autoGenerate = true)
public int id;
private String name;
@TypeConverters(DateConverter.class)
private Date createDate;
...
}
DateConverter.java:
import androidx.room.TypeConverter;
import java.util.Date;
public class DateConverter {
@TypeConverter
public static Date toDate(Long timestamp) {
return timestamp == null ? null : new Date(timestamp);
}
@TypeConverter
public static Long toTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
Solution 3:
I used the type converters described here (Article at Medium.com) and it worked:
@TypeConverter
public static List<MyObject> storedStringToMyObjects(String data) {
Gson gson = new Gson();
if (data == null) {
return Collections.emptyList();
}
Type listType = new TypeToken<List<MyObject>>() {}.getType();
return gson.fromJson(data, listType);
}
@TypeConverter
public static String myObjectsToStoredString(List<MyObject> myObjects) {
Gson gson = new Gson();
return gson.toJson(myObjects);
}
Solution 4:
Just in case if you need more clarity.
First create a converter general class like below.
class Converters {
@TypeConverter
fun fromGroupTaskMemberList(value: List<Comment>): String {
val gson = Gson()
val type = object : TypeToken<List<Comment>>() {}.type
return gson.toJson(value, type)
}
@TypeConverter
fun toGroupTaskMemberList(value: String): List<Comment> {
val gson = Gson()
val type = object : TypeToken<List<Comment>>() {}.type
return gson.fromJson(value, type)
}
}
Then add this converter in data base class just like,
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
Solution 5:
@TypeConverter
doesn't recognize List
class, so you should use ArrayList
instead, so you don't need additional wrapper for the list you want to persist.