如何用Retrofit+GSON+Room将API返回的子对象解析为字符串字段?
解决方案
方案1:自定义GSON TypeAdapter,在Retrofit解析时直接映射
这种方式能让Retrofit把API返回的JSON直接解析成你需要的Department实体,跳过手动映射的中间类。
步骤1:定义Room实体类
@Entity(tableName = "departments") data class Department( @PrimaryKey val id: Int, val name: String, val region: String // 直接存储region的nom值 // 其他字段... )
步骤2:编写GSON TypeAdapter
创建自定义TypeAdapter处理Department的反序列化,手动提取region.nom字段:
class DepartmentTypeAdapter : TypeAdapter<Department>() { override fun write(out: JsonWriter, value: Department) { // 若需序列化(如上传数据)可按需实现,此处省略 out.beginObject() out.name("id").value(value.id) out.name("name").value(value.name) out.name("region").value(value.region) out.endObject() } override fun read(`in`: JsonReader): Department { var id = 0 var name = "" var regionNom = "" `in`.beginObject() while (`in`.hasNext()) { when (`in`.nextName()) { "id" -> id = `in`.nextInt() "name" -> name = `in`.nextString() "region" -> { // 解析嵌套的region对象,提取nom字段 `in`.beginObject() while (`in`.hasNext()) { if (`in`.nextName() == "nom") { regionNom = `in`.nextString() } else { `in`.skipValue() // 跳过不需要的code字段 } } `in`.endObject() } else -> `in`.skipValue() // 跳过其他无关字段 } } `in`.endObject() return Department(id, name, regionNom) } }
步骤3:配置Retrofit使用TypeAdapter
在构建Retrofit实例时,将自定义TypeAdapter加入GSON配置:
val gson = GsonBuilder() .registerTypeAdapter(Department::class.java, DepartmentTypeAdapter()) .create() val retrofit = Retrofit.Builder() .baseUrl("你的API地址") .addConverterFactory(GsonConverterFactory.create(gson)) .build()
之后Retrofit请求返回的直接是Department实体,可直接插入Room数据库,无需手动映射。
方案2:用@JsonAdapter注解简化配置
若不想手动构建Gson实例,可直接在实体类上标注@JsonAdapter指定TypeAdapter:
@Entity(tableName = "departments") @JsonAdapter(DepartmentTypeAdapter::class) data class Department( @PrimaryKey val id: Int, val name: String, val region: String )
这样Retrofit的默认GsonConverterFactory会自动识别并使用该TypeAdapter,省去手动配置Gson的步骤。
额外说明:Room TypeConverter的适用场景
如果你的需求是在数据库中存储完整的Region对象,但查询时仅需nom字段,Room TypeConverter才适用:
- 定义Region数据类:
data class Region(val code: String, val nom: String)
- 修改Department实体的region字段为Region类型:
@Entity(tableName = "departments") data class Department( @PrimaryKey val id: Int, val name: String, val region: Region )
- 编写Room TypeConverter,实现Region与JSON字符串的互转:
class Converters { private val gson = Gson() @TypeConverter fun fromRegionToString(region: Region): String { return gson.toJson(region) } @TypeConverter fun fromStringToRegion(regionString: String): Region { return gson.fromJson(regionString, Region::class.java) } }
- 在RoomDatabase类上添加
@TypeConverters注解:
@Database(entities = [Department::class], version = 1) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun departmentDao(): DepartmentDao }
但此场景与你当前需求(实体类region为String类型)不符,因此更推荐前面的TypeAdapter方案。
内容的提问来源于stack exchange,提问作者Navalex
相关产品推荐
相关产品推荐

