Android room persistent library - TypeConverter error of error: Cannot figure out how to save field to database"

Android room persistent library - TypeConverter error of error: Cannot figure out how to save field to database"

由于错误,我无法在房间中创建类型转换器。我似乎正在按照文档进行所有操作。我想将列表转换为 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。所以我创建了以下转换器: 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);
}}

然后问题出在我放置 @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.

这是自发布 Room 以来我遇到的一个常见问题。 Room 不支持直接存储列表的能力,也不支持转换 to/from 列表的能力。支持POJO的转换和存储。

在这种情况下,解决方案很简单。而不是存储 List<CountryLang> 你想存储 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。

确保在使这些 changes.You 的其余配置正确后更新您的 RoomDatabase 版本。祝您在其余的房间持久性工作中愉快地狩猎。

尝试添加日期字段时遇到同样的错误:“无法弄清楚如何将此字段保存到数据库中”。必须为其添加转换器 class 并向字段添加 @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();
    }
}

@TypeConverter 无法识别 List class,因此您应该改用 ArrayList,这样您就不需要为您想要的列表添加额外的包装器坚持。

您还必须创建此 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;
}

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

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
    }
}

我使用了 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);
    }

有同样的问题。将 List 更改为 ArrayList。 list是接口,room无法存储

只需用 @Embedded 注释那个对象就解决了我的问题。像这样

@Embedded
private List<CrewListBean> crewList;

如果你想让自定义 class 兼容 (),你必须提供一个双向 @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 已经知道如何存储字符串。

With these converters, you can use your custom types in other queries, just as you would with primitive types

Location.kt

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

GL

Source

以防万一您需要更清楚的说明。

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

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)
}

}

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

@TypeConverters(Converters::class)

抽象class AppDatabase : RoomDatabase() {

我可能会迟到回答but.I有一些简单的解决方案我正在分享 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)

}

}

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

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

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

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

这应该放在导致 Room DB 错误的元素上

快乐编码

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

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

我使用它并且只需要定义字符串转换器(json)到对象 例如

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

可以来回整理 @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)