无法使用 @TypeConverter 在 LatLng 和 String 之间进行转换以存储在房间数据库中

发布于 2025-01-14 17:46:47 字数 1564 浏览 4 评论 0原文

我正在构建一个 Android 应用程序,需要在房间数据库中存储纬度和经度。

在我的内部,我使用 LatLng 类作为 Google Maps SDK 的一部分,但需要在 Room 中存储经度和纬度数据。

理想情况下,我想将数据保存在单列中,这就是为什么我使用 typeconvertor 将 LatLng 转换为字符串

由于 Room 无法直接存储 LatLng 对象,我希望使用 @TypeConverter 将数据作为字符串存储在 Room 数据库中。

不幸的是,出于某种原因,Room 不允许我在 LatLng 上使用 TypeConverter,如下面的错误所示:

构建失败

类型转换器的返回类型无效。 - stringToLatLng(java.lang.String)

当然,只要我提供将 LatLng 转换为 Room 接受并再次返回的类型的函数,它就应该允许这样做吗?我还尝试返回字符串和 LatLng 的可为空类型

感谢您帮助善良的互联网陌生人

Database.kt


@TypeConverters(Converters::class)
@Database(entities = [MyEntity::class], version = 1)
abstract class MyDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
} 

Converters.kt

class Converters{

    @TypeConverter
    fun latLngToString(latLng: LatLng) : String{
        return "(${latLng.latitude},${latLng.longitude}"
    }

    @TypeConverter
    fun stringToLatLng(string: String) : LatLng{
        val s = string.replace("(", "").replace(")", "")
        val list = s.split(",")
        return LatLng(list.first().toDouble(), list.last().toDouble())
    }
}

MyEntity.kt

@Entity
data class MyEntity(
    @PrimaryKey
    val iD: String,
    var location: LatLng,
    val timeStamp: Long
)

I'm building an android app that needs to store latitude and longitude in a room database.

Internally in my, am using the LatLng class as part of the Google Maps SDK, but need to store the longitude and latitude data in Room.

Ideally I want to save the data in a single column which is why I am using typeconvertor to convert LatLng to a String

As Room cannot store LatLng objects directly, I was hoping to use @TypeConverter to store the data as a string in the Room database.

Unfortunately Room is'nt letting me use the TypeConverter on LatLng for some reason, as shown in the error below:

Build Failed

Invalid return type for a type converter. - stringToLatLng(java.lang.String)

Surely so long as I provide functions to convert LatLng to a Type that Room accepts and back again it should allow this? I have also tried returning nullable types of both string and LatLng

Thanks for your help kind internet strangers

Database.kt


@TypeConverters(Converters::class)
@Database(entities = [MyEntity::class], version = 1)
abstract class MyDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
} 

Converters.kt

class Converters{

    @TypeConverter
    fun latLngToString(latLng: LatLng) : String{
        return "(${latLng.latitude},${latLng.longitude}"
    }

    @TypeConverter
    fun stringToLatLng(string: String) : LatLng{
        val s = string.replace("(", "").replace(")", "")
        val list = s.split(",")
        return LatLng(list.first().toDouble(), list.last().toDouble())
    }
}

MyEntity.kt

@Entity
data class MyEntity(
    @PrimaryKey
    val iD: String,
    var location: LatLng,
    val timeStamp: Long
)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

随遇而安 2025-01-21 17:46:48

根据您在其他答案中的评论,您的 TypeConverters 很好。我已经使用您的代码按原样测试了它们(除了输入的值可能无效)。因此,使用:-

    dao.insert(MyEntity("ME001", LatLng(101.12345678,202.87654321),System.currentTimeMillis()))
    dao.insert(MyEntity("ME002", LatLng(678.12345678,321.87654321),System.currentTimeMillis()))

然后使用:-

    for(me: MyEntity in dao.getAllMyEntities()) {
        Log.d("DBINFO","MyEntity is ${me.iD} \n\tLATITUDE is ${me.location.latitude} \n\tLONGITUDE is ${me.location.longitude} \n\tTIMESTAMP is ${me.timeStamp}")
    }

getAllMyEntities 是:-

@Query("SELECT * FROM myentity")
fun getAllMyEntities(): List<MyEntity>

结果日志产生:-

2022-03-18 05:32:13.279 13017-13017/a.a.so71506139kotlinroomlatlngissue D/DBINFO: MyEntity is ME001 
        LATITUDE is 90.0 
        LONGITUDE is -157.12345678999998 
        TIMESTAMP is 1647541933213
2022-03-18 05:32:13.279 13017-13017/a.a.so71506139kotlinroomlatlngissue D/DBINFO: MyEntity is ME002 
        LATITUDE is 90.0 
        LONGITUDE is -38.12345678999998 
        TIMESTAMP is 1647541933215
  • 可以看到,输入的值与输出的值不匹配,但它们确实与存储的值匹配(所以我假设已经进行了一些转换/操作)由 LatLng 类承担)。

通过应用程序检查的数据库为:-

在此处输入图像描述


但是,当最初将代码应用到现有项目时,我遇到了 无效退货类型

我所做的就是考虑使用替代的 MyLatLng (2 个双精度)和 TypeConverters 使用:-

import com.google.android.gms.maps.model.LatLng

class MyLatLng(lat: Double, lng: Double) {

    var latitude: Double = lat
    var longitude: Double = lng

    /* <<<<<<<<<< Added AFTER successful compile >>>>>>>>>>*/
    fun getAsLatLng(): LatLng {
        return LatLng(latitude,longitude)
    }

}

和 TypeConverters :-

@TypeConverter
fun myLatLngToString(latLng: MyLatLng) : String{
    return "(${latLng.latitude},${latLng.longitude}"
}

@TypeConverter
fun stringToMyLatLng(string: String) : MyLatLng{
    val s = string.replace("(", "").replace(")", "")
    val list = s.split(",")
    return MyLatLng(list.first().toDouble(), list.last().toDouble())
}

将位置更改为MyLatLng 而不是 LatLng 编译得很好,所以我然后添加了将 MyLatLng 转换为 LatLng 的函数,然后编译得很好。

然后我恢复到原来的 LatLng 并注释掉 MyLatLng 转换器,然后NO invalid return type失败。

然后,我仅使用您的代码和附加内容创建了一个全新的项目,以使其能够运行。它编译正常并运行(如上面的结果所示)。

新项目中使用的完整代码(带有导入):-

MyEntity

import androidx.room.Entity
import androidx.room.PrimaryKey
import com.google.android.gms.maps.model.LatLng

@Entity
data class MyEntity(
    @PrimaryKey
    val iD: String,
    var location: LatLng,
    val timeStamp: Long
)

Converters

import androidx.room.TypeConverter
import com.google.android.gms.maps.model.LatLng

class Converters{

    @TypeConverter
    fun latLngToString(latLng: LatLng) : String{
        return "(${latLng.latitude},${latLng.longitude}"
    }

    @TypeConverter
    fun stringToLatLng(string: String) : LatLng{
        val s = string.replace("(", "").replace(")", "")
        val list = s.split(",")
        return LatLng(list.first().toDouble(), list.last().toDouble())
    }
}

AllDao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query

@Dao
interface AllDao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(myEntity: MyEntity)
    @Query("SELECT * FROM myentity")
    fun getAllMyEntities(): List<MyEntity>
}

TheDatabase

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

@Database(entities = [MyEntity::class], version = 1, exportSchema = false)
@TypeConverters(value = [Converters::class])
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,"the_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

主要活动

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.google.android.gms.maps.model.LatLng

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)
        db.openHelper.writableDatabase
        dao = db.getAllDao()
        dao.insert(MyEntity("ME001", LatLng(101.12345678,202.87654321),System.currentTimeMillis()))
        dao.insert(MyEntity("ME002", LatLng(678.12345678,321.87654321),System.currentTimeMillis()))
        for(me: MyEntity in dao.getAllMyEntities()) {
            Log.d("DBINFO","MyEntity is ${me.iD} \n\tLATITUDE is ${me.location.latitude} \n\tLONGITUDE is ${me.location.longitude} \n\tTIMESTAMP is ${me.timeStamp}")
        }
    }
}

As per your comment in the other answer, your TypeConverters are fine. I have tested them using your code as is (except that that values input are perhaps invalid). So using:-

    dao.insert(MyEntity("ME001", LatLng(101.12345678,202.87654321),System.currentTimeMillis()))
    dao.insert(MyEntity("ME002", LatLng(678.12345678,321.87654321),System.currentTimeMillis()))

and then using :-

    for(me: MyEntity in dao.getAllMyEntities()) {
        Log.d("DBINFO","MyEntity is ${me.iD} \n\tLATITUDE is ${me.location.latitude} \n\tLONGITUDE is ${me.location.longitude} \n\tTIMESTAMP is ${me.timeStamp}")
    }

With getAllMyEntities being:-

@Query("SELECT * FROM myentity")
fun getAllMyEntities(): List<MyEntity>

The resultant log produces:-

2022-03-18 05:32:13.279 13017-13017/a.a.so71506139kotlinroomlatlngissue D/DBINFO: MyEntity is ME001 
        LATITUDE is 90.0 
        LONGITUDE is -157.12345678999998 
        TIMESTAMP is 1647541933213
2022-03-18 05:32:13.279 13017-13017/a.a.so71506139kotlinroomlatlngissue D/DBINFO: MyEntity is ME002 
        LATITUDE is 90.0 
        LONGITUDE is -38.12345678999998 
        TIMESTAMP is 1647541933215
  • as can be see the values input do not match the values output BUT they do match the stored values (so I assume some conversion/manipulation has been undertaken by the LatLng class).

The database, via App Inspection is:-

enter image description here


HOWEVER, when initially applying your code to an existing project I had the issue with the invalid return type.

What I did was to then look at using an alternative MyLatLng (2 doubles) and TypeConverters using:-

import com.google.android.gms.maps.model.LatLng

class MyLatLng(lat: Double, lng: Double) {

    var latitude: Double = lat
    var longitude: Double = lng

    /* <<<<<<<<<< Added AFTER successful compile >>>>>>>>>>*/
    fun getAsLatLng(): LatLng {
        return LatLng(latitude,longitude)
    }

}

and TypeConverters :-

@TypeConverter
fun myLatLngToString(latLng: MyLatLng) : String{
    return "(${latLng.latitude},${latLng.longitude}"
}

@TypeConverter
fun stringToMyLatLng(string: String) : MyLatLng{
    val s = string.replace("(", "").replace(")", "")
    val list = s.split(",")
    return MyLatLng(list.first().toDouble(), list.last().toDouble())
}

Changing location to be a MyLatLng rather than LatLng compiled fine, so I then added the function to convert a MyLatLng to a LatLng, this then compiled fine.

I then reverted to the original LatLng and commented out the MyLatLng converters and then NO invalid return type failure.

I then created a brand new project just with your code and extras to enable it to be run. It compiled OK and ran (as the results above show).

The full code used in the new project (with the imports):-

MyEntity

import androidx.room.Entity
import androidx.room.PrimaryKey
import com.google.android.gms.maps.model.LatLng

@Entity
data class MyEntity(
    @PrimaryKey
    val iD: String,
    var location: LatLng,
    val timeStamp: Long
)

Converters

import androidx.room.TypeConverter
import com.google.android.gms.maps.model.LatLng

class Converters{

    @TypeConverter
    fun latLngToString(latLng: LatLng) : String{
        return "(${latLng.latitude},${latLng.longitude}"
    }

    @TypeConverter
    fun stringToLatLng(string: String) : LatLng{
        val s = string.replace("(", "").replace(")", "")
        val list = s.split(",")
        return LatLng(list.first().toDouble(), list.last().toDouble())
    }
}

AllDao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query

@Dao
interface AllDao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(myEntity: MyEntity)
    @Query("SELECT * FROM myentity")
    fun getAllMyEntities(): List<MyEntity>
}

TheDatabase

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

@Database(entities = [MyEntity::class], version = 1, exportSchema = false)
@TypeConverters(value = [Converters::class])
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,"the_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

MainActivity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.google.android.gms.maps.model.LatLng

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)
        db.openHelper.writableDatabase
        dao = db.getAllDao()
        dao.insert(MyEntity("ME001", LatLng(101.12345678,202.87654321),System.currentTimeMillis()))
        dao.insert(MyEntity("ME002", LatLng(678.12345678,321.87654321),System.currentTimeMillis()))
        for(me: MyEntity in dao.getAllMyEntities()) {
            Log.d("DBINFO","MyEntity is ${me.iD} \n\tLATITUDE is ${me.location.latitude} \n\tLONGITUDE is ${me.location.longitude} \n\tTIMESTAMP is ${me.timeStamp}")
        }
    }
}
み零 2025-01-21 17:46:47

您需要将 LatLng 存储到 String 有什么具体原因吗?就我而言,我只是用双纬度和经度保存它。

@Entity(tableName = "places")
data class PlaceResponse(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "placeId")
@SerializedName("placeId")
@Expose
var placeId: Int,

@SerializedName("title")
@ColumnInfo(name = "title")
var title: String? = null,

@ColumnInfo(name = "lat")
var lat: Double = 0.0,

@ColumnInfo(name = "lng")
var lng: Double = 0.0
)

Is there any specific reason that you need to store LatLng to String? In my case I just save it with double Latitude and Longitude.

@Entity(tableName = "places")
data class PlaceResponse(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "placeId")
@SerializedName("placeId")
@Expose
var placeId: Int,

@SerializedName("title")
@ColumnInfo(name = "title")
var title: String? = null,

@ColumnInfo(name = "lat")
var lat: Double = 0.0,

@ColumnInfo(name = "lng")
var lng: Double = 0.0
)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文