使用Kotlin宽松Json序列化时如何上报接收到的未知Json字段
实现方案:kotlinx.serialization 原生捕获未知 JSON 字段
完全不需要引入额外第三方库,基于 kotlinx.serialization 原生能力即可同时保留宽松解析配置、捕获所有未知字段,具体实现如下:
现有配置保留
你原有的 Json 配置不需要修改:
val jsonConfig = Json { ignoreUnknownKeys = true isLenient = true }
核心实现逻辑
通过自定义 Retrofit Converter.Factory,在反序列化前先读取完整 JSON 结构,对比目标类的已知序列化字段,过滤出未知字段上报后再执行正常反序列化逻辑即可。
自定义 Converter 代码
import kotlinx.serialization.descriptors.elementNames import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.serializer import okhttp3.ResponseBody import retrofit2.Converter import retrofit2.Retrofit import retrofit2.converter.kotlinx.serialization.KotlinxSerializationConverterFactory import java.lang.reflect.Type import kotlin.reflect.KClass import kotlin.reflect.jvm.kotlin class UnknownKeyCaptureConverterFactory( private val json: Json, private val onUnknownKeysFound: (clazz: KClass<*>, unknownKeys: Map<String, JsonElement>) -> Unit ) : Converter.Factory() { private val delegateFactory = KotlinxSerializationConverterFactory.create(json) override fun responseBodyConverter( type: Type, annotations: Array<out Annotation>, retrofit: Retrofit ): Converter<ResponseBody, *>? { val delegateConverter = delegateFactory.responseBodyConverter(type, annotations, retrofit) ?: return null val kClass = (type as? Class<*>)?.kotlin ?: return delegateConverter val serializer = json.serializersModule.serializer(type) val knownKeys = serializer.descriptor.elementNames.toSet() return Converter<ResponseBody, Any> { body -> val jsonString = body.string() val jsonElement = json.parseToJsonElement(jsonString) if (jsonElement is JsonObject) { val unknownKeys = jsonElement.filterKeys { it !in knownKeys } if (unknownKeys.isNotEmpty()) { onUnknownKeysFound(kClass, unknownKeys) } } json.decodeFromJsonElement(serializer, jsonElement) } } }
替换 Retrofit 配置
初始化 Retrofit 时用自定义的 Converter 替换原生的 kotlinx.serialization Converter:
val retrofit = Retrofit.Builder() .baseUrl("你的接口基地址") .addConverterFactory( UnknownKeyCaptureConverterFactory(jsonConfig) { clazz, unknownKeys -> // 此处执行未知字段上报逻辑,如打印日志、上报埋点 println("类 ${clazz.simpleName} 检测到未知字段:$unknownKeys") // 你的上报代码 } ) // 其他配置... .build()
注意事项
- 上述实现默认只捕获最外层 JSON 对象的未知字段,如果需要捕获嵌套对象的未知字段,可扩展逻辑递归遍历所有嵌套的 JsonObject,和对应嵌套类的字段列表做对比即可
- 常规接口响应大小的场景下,该方案的性能损耗可以忽略,如果需要处理超大响应,可改用 kotlinx.serialization 流式解析 API 配合自定义 Decoder 实现,降低内存占用
- 原有宽松解析、忽略未知字段的逻辑完全保留,不会影响现有业务的正常运行
内容的提问来源于stack exchange,提问作者Hector
相关产品推荐
相关产品推荐

