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

Apollo Kotlin GraphQL使用Fragment时Moshi转换无法识别Fragment类型

解决Apollo Kotlin中Fragment在Moshi转换时的字段映射问题

问题核心

使用Fragment编写GraphQL查询后,Apollo生成的Kotlin类会将Fragment字段包装在userDataFields嵌套对象中,但实际API返回的响应是平铺展开Fragment字段(GraphQL服务器会自动内联展开Fragment),导致纯Moshi解析时找不到userDataFields字段,最终该属性为null。

解决方案

1. 使用Apollo Kotlin自带的响应解析器(推荐)

Apollo内置了专门处理GraphQL响应(包括Fragment映射)的解析逻辑,不需要用纯Moshi解析。单元测试中可以直接用Apollo提供的工具来解析JSON响应:

// 读取本地JSON文件内容
val jsonString = File("your-json-file-path").readText()

// 创建ApolloClient实例(测试无需真实服务器地址)
val apolloClient = ApolloClient.Builder()
    .serverUrl("http://dummy-url.com")
    .build()

// 用Apollo的JsonParser解析响应到生成的Data类
val data = apolloClient.apolloStore.jsonParser.parse(
    UserDetailsSearchQuery.Data.serializer(),
    jsonString
)

这种方式会自动处理Fragment的字段映射,将平铺的JSON字段正确填充到userDataFields中。

2. 修改Apollo代码生成配置,取消Fragment字段嵌套

如果坚持要用纯Moshi解析,可以修改Apollo的Gradle配置,让生成的Kotlin类直接平铺Fragment字段,避免嵌套:
在模块的build.gradle.kts(或build.gradle)中添加:

apollo {
    generateKotlinModels.set(true)
    // 使用flat模式生成模型,Fragment字段会直接合并到父类中
    codegenModels.set("flat")
}

重新生成代码后,FetchData类会直接包含id、name、profile等字段,不再有userDataFields嵌套对象,此时用Moshi解析平铺的JSON就能正常映射。

3. 自定义Moshi Adapter手动映射字段

如果无法修改配置或使用Apollo解析器,可以编写自定义Moshi Adapter,将平铺的JSON字段手动组装到userDataFields中:

class FetchDataAdapter(private val moshi: Moshi) {
    private val profileAdapter = moshi.adapter(Profile::class.java)

    @FromJson
    fun fromJson(json: Map<String, Any?>): FetchData {
        return FetchData(
            __typename = json["__typename"] as String,
            userDataFields = UserDataFields(
                id = json["id"] as String,
                name = json["name"] as String,
                profile = profileAdapter.fromJsonValue(json["profile"] as Map<String, Any?>)!!
            )
        )
    }
}

然后在构建Moshi时添加这个Adapter:

val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .add(FetchDataAdapter(Moshi.Builder().add(KotlinJsonAdapterFactory()).build()))
    .build()

// 之后用这个Moshi解析即可
val mockResponse = typeFromLocalJson<UserDetailsSearchQuery.Data>(
    "your-json-file-path",
    moshi
).getOrHandle { throw it }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 14:47:25