Moshi如何实现动态自定义字段名以适配不同运行环境
Moshi动态自定义字段名实现方案
Moshi 原生@Json(name = "xxx")注解的参数要求是编译期常量,无法直接实现运行时动态切换字段名的需求。在注解参数内传入动态取值逻辑的写法不符合注解语法约束,注解值会在编译阶段被固定,自然无法达到动态切换的效果。
以下是3种经过验证的可落地方案,可根据自己的场景选择:
- 单字段/少量字段需要定制差异化动态名称:优先用自定义
JsonAdapter - 全局字段名有统一的环境差异规则:按环境构建多套Moshi实例
- 仅反序列化需要兼容多环境字段名,序列化输出固定名称:用注解自带的alternate属性
方案1:自定义字段级JsonAdapter
针对需要动态切换名称的字段单独实现适配器,内部根据当前运行环境选择读写的JSON键,序列化和反序列化的逻辑都可以完全自定义:
// 全局环境标识,可从构建配置、配置中心读取,示例为TEST/PROD两个环境 enum class Env { TEST, PROD } val currentRuntimeEnv = getCurrentEnv() // 自行实现环境读取逻辑 class DynamicFieldAdapter { @ToJson fun toJson(writer: JsonWriter, fieldValue: String?) { fieldValue ?: return // 根据当前环境选择序列化输出的键名 val outputKey = when(currentRuntimeEnv) { Env.TEST -> "username_test" Env.PROD -> "username" } writer.name(outputKey).value(fieldValue) } @FromJson fun fromJson(reader: JsonReader): String? { val jsonMap = reader.readJsonValue() as? Map<*, *> ?: return null // 优先读取当前环境对应的键,兜底读取其他环境的键做兼容 return when(currentRuntimeEnv) { Env.TEST -> jsonMap["username_test"] ?: jsonMap["username"] Env.PROD -> jsonMap["username"] ?: jsonMap["username_test"] }?.toString() } } // 实体类字段上指定适配器即可 data class User( @JsonAdapter(DynamicFieldAdapter::class) val username: String ) // 初始化Moshi时注册适配器 val moshi = Moshi.Builder() .add(DynamicFieldAdapter()) .build()
如果有多个字段需要动态切换,可以把适配器写成通用的,通过自定义注解给字段标记不同环境对应的键名,适配器里反射读取注解配置即可,不用每个字段写一个适配器。
方案2:多环境独立Moshi实例
如果不同环境的字段名差异是全局统一规则(比如测试环境所有JSON字段统一加_test后缀),不需要单字段定制,可以在应用初始化时,为每个环境构建独立的Moshi实例,配置对应的FieldNamingStrategy,运行时直接取当前环境对应的Moshi实例做序列化/反序列化即可,不需要额外修改实体类代码。
方案3:@Json alternate 轻量兼容
如果业务只需要反序列化时兼容不同环境的字段名,序列化时统一输出固定字段名,不需要额外写适配器,直接在@Json注解的alternate参数里配置所有可能的字段名即可,Moshi反序列化时会自动识别所有配置的键:
// 序列化固定输出username,反序列化时可识别username、username_test两个键 @Json(name = "username", alternate = ["username_test"]) val username: String
内容的提问来源于stack exchange,提问作者OvenChow
相关产品推荐
相关产品推荐

