You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Room中如何使用JSON_EXTRACT?Room是否支持该函数?

解决Room中使用JSON_EXTRACT报错的问题

嘿,我来帮你梳理下这个问题的原因和解决办法~

为什么会报错?

你遇到的android.database.sqlite.SQLiteException: no such function: json_extract,核心原因是Android系统自带的SQLite版本不够。json_extract是SQLite 3.18.0才新增的JSON处理函数,而Android 7.0(API 24)及以上的系统才默认搭载这个版本的SQLite;如果你的App运行在API 23或更低版本的设备上,就会出现“找不到函数”的错误。

Room本身并没有限制使用JSON函数,它完全依赖底层系统提供的SQLite支持能力,所以不是Room不支持,是低版本系统不支持。

两种可行的解决方案

方法1:用Room TypeConverter解析JSON(推荐,全版本兼容)

这种方法不需要依赖SQLite的JSON函数,而是把JSON字符串转换成Java/Kotlin对象,直接操作对象属性来查询,兼容性拉满。

步骤走一遍:

  1. 先定义对应JSON结构的数据类,匹配你products数组里的元素:
// 对应你JSON里的$[0].qty_case字段
data class Product(
    @SerializedName("qty_case") val qtyCase: Int
)
  1. 写一个TypeConverter,实现JSON字符串和List<Product>的互转(这里用Gson举例,Moshi等库也可以):
class ProductConverter {
    private val gson = Gson()

    @TypeConverter
    fun fromJson(jsonStr: String): List<Product> {
        // 先处理你原查询里的replace(tfo.products, '\','')逻辑
        val cleanedJson = jsonStr.replace("\\", "")
        return gson.fromJson(cleanedJson, object : TypeToken<List<Product>>() {}.type)
    }

    @TypeConverter
    fun toJson(productList: List<Product>): String {
        return gson.toJson(productList)
    }
}
  1. 在你的Room Database类上添加@TypeConverters注解,让Room知道这个转换器:
@Database(entities = [Tfo::class], version = 1)
@TypeConverters(ProductConverter::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun tfoDao(): TfoDao
}
  1. 修改DAO里的查询语句,直接访问转换后的对象属性就行:
@Query("""
    SELECT SUM(products[0].qtyCase) as QtyCase 
    FROM tfo 
    WHERE strftime('%m', dateCreated) = strftime('%m', 'now', '0 months') 
      AND armstrong2CustomersId = 'OPDB00140428AU' 
      AND armstrong2TfoId = '9466c9d5-7d58-4d92-96e0-010662aa0055'
""")
fun getTotalQtyCase(): Int?

这里的products是你Tfo实体类里定义的List<Product>类型字段,Room会自动用转换器处理JSON的转换。

方法2:硬刚SQL层面的解决方案

如果你一定要用SQL里的JSON操作,可以选下面两种子方案:

  • 子方案A:只支持高版本设备
    直接保留原查询里的json_extract,但把App的最低SDK版本设为24:

    android {
        defaultConfig {
            minSdkVersion 24
        }
    }
    

    这样就能保证运行的设备都支持这个函数。

  • 子方案B:自定义JSON解析函数
    要兼容低版本的话,可以给Room添加自定义函数来替代json_extract:
    先在构建Room数据库的时候注册自定义函数:

    val db = Room.databaseBuilder(context, AppDatabase::class.java, "app_database")
        .addCallback(object : RoomDatabase.Callback() {
            override fun onCreate(db: SupportSQLiteDatabase) {
                super.onCreate(db)
                // 注册自定义的json_extract函数
                db.execSQL("""
                    CREATE FUNCTION json_extract(json TEXT, path TEXT) 
                    RETURNS TEXT 
                    LANGUAGE JAVA 
                    AS 'com.yourpackage.JsonUtils.extractJsonValue';
                """)
            }
        })
        .build()
    

    然后实现对应的静态方法来解析JSON:

    object JsonUtils {
        @JvmStatic
        fun extractJsonValue(json: String?, path: String?): String? {
            if (json.isNullOrEmpty() || path.isNullOrEmpty()) return null
            // 先处理转义字符
            val cleanedJson = json.replace("\\", "")
            val gson = Gson()
            val jsonElement = JsonParser.parseString(cleanedJson)
            
            // 简单解析路径,比如处理$[0].qty_case这种格式
            val pathParts = path.removePrefix("$").split(".")
            var currentElement = jsonElement
            for (part in pathParts) {
                currentElement = when {
                    part.matches(Regex("\\[\\d+\\]")) -> {
                        // 数组索引
                        val index = part.substring(1, part.length - 1).toInt()
                        currentElement.asJsonArray[index]
                    }
                    else -> {
                        // 对象字段
                        currentElement.asJsonObject.get(part)
                    }
                }
            }
            return currentElement.asString
        }
    }
    

    这个方法需要自己实现路径解析逻辑,适合简单场景,复杂路径可能需要更完善的处理。

对你原查询的小优化

原查询里的(select distinct replace(tfo.products, '\',''))其实可以简化,因为每个tfo记录的products都是唯一的,直接用replace(tfo.products, '\','')就行,没必要加select distinct。用TypeConverter方案的话,这个替换逻辑可以统一放在转换器里,更整洁。

内容的提问来源于stack exchange,提问作者Xiao King

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:42:31