Android 房间持久库 - TypeConverter 错误错误:无法弄清楚如何将字段保存到数据库”

Posted

技术标签:

【中文标题】Android 房间持久库 - TypeConverter 错误错误:无法弄清楚如何将字段保存到数据库”【英文标题】:Android room persistent library - TypeConverter error of error: Cannot figure out how to save field to database" 【发布时间】:2017-11-18 19:50:33 【问题描述】:

由于错误,我无法在房间中创建 typeConverter。我似乎遵循文档中的所有内容。我想将列表转换为 json 字符串。让我们看看我的实体:

      @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;
    


country_lang 是我想转换为字符串 json 的内容。所以我创建了以下转换器: 转换器.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);

那么问题出在我放置@TypeConverters(Converters.class) 的任何地方,我一直收到错误消息。但正式这是我放置注释以注册 typeConverter 的地方:

@Database(entities = CountryModel.class, version = 1 ,exportSchema = false)
@TypeConverters(Converters.class)
public abstract class MYDatabase extends RoomDatabase 
    public abstract CountriesDao countriesDao();

我得到的错误是:

Error:(58, 31) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.

【问题讨论】:

您是否也有从StringList&lt;CountryLang&gt; 的转换器?我在您的代码中没有看到它。 Room 也需要它才能读回数据。 我试过了,但没有帮助。同样的错误 用于列表的 TypeConverter 确实有效。如果它不适合您,您可能忘记添加反向转换方法。 @TypeConverter public static List&lt;CountryLang&gt; jsonToCountryLang(String json) 【参考方案1】:

这是我在 Room 发布后遇到的常见问题。 Room 不支持直接存储列表的能力,也不支持与列表相互转换的能力。支持POJO的转换和存储。

在这种情况下,解决方案很简单。而不是存储List&lt;CountryLang&gt;,而是存储CountryLangs(注意's')

我在这里做了一个简单的解决方案示例:

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;
    

这个 POJO 是你之前的对象的反转。它是一个存储语言列表的对象。而不是存储您的语言的对象列表。

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;
    

此转换器获取字符串列表并将它们转换为逗号分隔的字符串以存储在单个列中。当它从 SQLite 数据库中获取字符串以转换回来时,它会用逗号分割列表,并填充 CountryLangs。

确保在进行这些更改后更新您的 RoomDatabase 版本。您的其余配置正确。与您余下的 Room 持久性工作一起愉快地狩猎。

【讨论】:

Jack,你知道如何存储 Enum 列表吗? 我去看看,把它作为问题发布,以便其他人能够轻松找到问题+答案。帮助社区:) 我已经发布了我的回复 最近我遇到了这种方法与@Embedded结合的问题。房间说Cannot figure out how to save this field into database。有人知道为什么会发生这种情况吗?【参考方案2】:

在尝试添加日期字段时出现同样的错误:“无法弄清楚如何将此字段保存到数据库中”。必须为其添加一个转换器类并将@TypeConverters 注解添加到字段。

例子:

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();
    

【讨论】:

请注意,我还必须根据 Google 的文章将 @TypeConverters(DateConverter.class) 添加到房间数据库类:developer.android.com/training/data-storage/room/…【参考方案3】:

我使用了here (Article at Medium.com) 中描述的类型转换器,它起作用了:

@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);
    

【讨论】:

在字段中缓存new Gson。它是一个沉重的对象并且需要很长时间才能创建,您不希望每次调用转换器时都创建一个新实例。类型标记也是如此。 这是最有效的答案 是的,这确实是这里最好的答案,因为它使用Gson进行序列化/反序列化,而底层结构当然是Json。这适用于任何对象。我相当确定如果对象具有包含逗号的强字段,则接受(并且更赞成)的答案会产生一些意想不到的行为。此外,@EugenPechanec 指出的内容很重要,但我不会更改答案,因为这会使答案复杂化,并有损于理解这里的主要概念。【参考方案4】:

【讨论】:

我们还需要其他配置吗?我将List 更改为ArrayList 但仍然收到error: Cannot figure out how to save this field into database. You can consider adding a type converter for it. private java.util.ArrayList&lt;java.lang.String&gt; notificationTypes; ^ @PhanVanLinh 我们使用的是Kotlin,所以使用kotlin.collections.ArrayList 对自定义对象来说很好。对于kotlin.String,不需要使用ArrayList,用简单的kotlin.collections.List就可以了 即使在我的自定义对象上使用kotlin.collections.ArrayList 后,我仍然会看到错误。【参考方案5】:

只需使用 @Embedded 注释该对象即可解决我的问题。像这样

@Embedded
private List<CrewListBean> crewList;

【讨论】:

实体和 POJO 必须有一个可用的公共构造函数。您可以有一个空的构造函数或参数与字段匹配的构造函数(按名称和类型)。 - java.util.Listerror:实体和 POJO 必须有一个可用的公共构造函数。您可以有一个空的构造函数或参数与字段匹配的构造函数(按名称和类型)。 - java.util.List 这可能是一个解决方案,请参阅developer.android.com/training/data-storage/room/…。目前我正在尝试 1:1 加入两个表,因为 JSON 有嵌套字段。【参考方案6】:

以防万一您需要更清楚。

首先创建一个转换器通用类,如下所示。

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)

然后像这样在数据库类中添加这个转换器,

@TypeConverters(Converters::class)

抽象类 AppDatabase : RoomDatabase()

【讨论】:

【参考方案7】:

我可能会迟到回答但我有一些简单的解决方案我正在分享处理一些基本要求的 TypeConverters 调用

class RoomConverters 
//for date and time convertions
@TypeConverter
fun calendarToDateStamp(calendar: Calendar): Long = calendar.timeInMillis

@TypeConverter
fun dateStampToCalendar(value: Long): Calendar =
    Calendar.getInstance().apply  timeInMillis = value 

//list of cutome object in your database
@TypeConverter
fun saveAddressList(listOfString: List<AddressDTO?>?): String? 
    return Gson().toJson(listOfString)


@TypeConverter
fun getAddressList(listOfString: String?): List<AddressDTO?>? 
    return Gson().fromJson(
        listOfString,
        object : TypeToken<List<String?>?>() .type
    )


/*  for converting List<Double?>?  you can do same with other data type*/
@TypeConverter
fun saveDoubleList(listOfString: List<Double>): String? 
    return Gson().toJson(listOfString)


@TypeConverter
fun getDoubleList(listOfString: List<Double>): List<Double> 
    return Gson().fromJson(
        listOfString.toString(),
        object : TypeToken<List<Double?>?>() .type
    )


// for converting the json object or String into Pojo or DTO class
@TypeConverter
fun toCurrentLocationDTO(value: String?): CurrentLocationDTO 
    return  Gson().fromJson(
        value,
        object : TypeToken<CurrentLocationDTO?>() .type
    )


@TypeConverter
fun fromCurrentLocationDTO(categories: CurrentLocationDTO?): String 
    return Gson().toJson(categories)




您必须编写自己的类并在此处解析,然后将其添加到您的 AppDatabase 类中

@Database(
        entities = [UserDTO::class],
        version = 1, exportSchema = false
         ) 
@TypeConverters(RoomConverters::class)
@Singleton
abstract class AppDatabase : RoomDatabase() 

【讨论】:

【参考方案8】:

Kotlin 示例(不好但简单,TODO: json):

import android.arch.persistence.room.*

@Entity(tableName = "doctor")
data class DoctorEntity(

    @PrimaryKey
    @ColumnInfo(name = "id") val id: Long,

    @ColumnInfo(name = "contactName") val contactName: String?,

    @TypeConverters(CategoryConverter::class)
    @ColumnInfo(name = "categories") val categories: Categories?,

    @TypeConverters(CategoryConverter::class)
    @ColumnInfo(name = "languages") val languages: Categories?
) 

data class Categories(
    val categories: ArrayList<Long> = ArrayList()
)

class CategoryConverter 

    @TypeConverter
    fun toCategories(value: String?): Categories 
        if (value == null || value.isEmpty()) 
            return Categories()
        

        val list: List<String> = value.split(",")
        val longList = ArrayList<Long>()
        for (item in list) 
            if (!item.isEmpty()) 
                longList.add(item.toLong())
            
        
        return Categories(longList)
    

    @TypeConverter
    fun toString(categories: Categories?): String 

        var string = ""

        if (categories == null) 
            return string
        

        categories.categories.forEach 
            string += "$it,"
        
        return string
    

【讨论】:

感谢您的回答,在迁移到新房间桌版本时添加此类型怎么样?遇到问题如何在迁移操作中定义这个类别对象的类型,比如这个 database.execSQL【参考方案9】:

您还必须创建此TypeConverter,它将您的List 转换为String

@TypeConverter
public List<CountryLang> toCountryLangList(String countryLangString) 
    if (countryLangString == null) 
        return (null);
    
    Gson gson = new Gson();
    Type type = new TypeToken<List<CountryLang>>() .getType();
    List<CountryLang> countryLangList = gson.fromJson(countryLangString, type);
    return countryLangList;

有关更多信息,您还可以查看我的另一个answer。

【讨论】:

【参考方案10】:

如果您想让自定义类兼容 (different from the supported ones),您必须提供双向 @TypeConverter 转换器,它将自定义类转换为 Room 已知的类,反之亦然。

例如,如果我们想保留 LatLng 的实例:

前置条件:implementation("com.squareup.moshi:moshi-kotlin:1.9.2")

Converters.kt

@TypeConverter
fun stringToLatLng(input: String?): LatLng? =
        input?.let  Moshi.Builder().build().adapter(LatLng::class.java).fromJson(it) 

@TypeConverter
fun latLngToString(input: LatLng): String? =
        Moshi.Builder().build().adapter(LatLng::class.java).toJson(input)

Room 已经知道如何存储字符串了。

使用这些转换器,您可以在其他方面使用您的自定义类型 查询,就像使用原始类型一样

位置.kt

@Entity
data class Location(private val location: LatLng?)

GL

Source

【讨论】:

【参考方案11】:

这里给出的解决方案是不完整的,一旦你完成了在接受的答案中给出的过程,你还需要在你的实体类中添加另一个注释

@TypeConverters(Converter.class)
private List<String> brandId;

这应该放在导致房间数据库错误的元素上

快乐编码

【讨论】:

【参考方案12】:

您可以避免使用此类型转换器对所有对象进行对象到字符串(json)类型转换器

@TypeConverter
    fun objectToJson(value: Any?) = Gson().toJson(value)
 

我使用它并且必须只定义字符串(json)到对象的转换器 例如。

@TypeConverter
    fun stringToPersonObject(string: String?): Person? 
        return Gson().fromJson(string, Person::class.java)
    

【讨论】:

【参考方案13】:

可以将@TypeConverter 组织为@TypeConverters

public class DateConverter 
    @TypeConverter
    public long from(Date value) 
        return value.getTime();
    
    @TypeConverter
    public Date to(long value) 
        return new Date(value);
    

然后将它们应用于以下字段:

@TypeConverters(DateConverter.class)

【讨论】:

【参考方案14】:

有同样的问题。将List 更改为ArrayList。列表是接口,房间无法存储。

【讨论】:

以上是关于Android 房间持久库 - TypeConverter 错误错误:无法弄清楚如何将字段保存到数据库”的主要内容,如果未能解决你的问题,请参考以下文章

如何在房间持久性库中使用外键

如何在房间持久性库中插入图像?

房间持久化库和内容提供者

Android Room持久性库@Update无效

房间持久性库。删除所有

具有新线程和数据绑定问题的房间持久库