ScalaPB生成的不同Proto枚举如何实现无缝互转
ScalaPB 跨Proto枚举无缝转换实现方案
问题背景
在两个独立proto文件中定义了结构完全一致的枚举:
enum Some { OPTIONA = 0; OPTIONB = 1; OPTIONC = 2; } enum SomeOther { OPTIONA = 0; OPTIONB = 1; OPTIONC = 2; }
经ScalaPB生成Scala代码后,两个枚举均继承scalapb.GeneratedEnum,对应伴生对象继承scalapb.GeneratedEnumCompanion。需求是不手动编写模式匹配逻辑,实现两个枚举的双向互转:
- 最初尝试用Chimney的
transformInto方法实现转换,触发类型歧义错误 - 自行编写隐式转换类时,因未正确处理伴生对象的隐式解析,逻辑无法正常运行
方案1:配置Chimney支持ScalaPB枚举自动转换
Chimney报类型歧义的核心原因是默认的sealed类型派生逻辑,会尝试对两个枚举的case做一一匹配,而两个枚举的case名称完全一致但类型不同,导致推导冲突。只需要提前给Chimney提供优先级更高的ScalaPB枚举通用转换器即可解决:
import scalapb._ import io.scalachimney._ // 全局通用ScalaPB枚举转换器,优先级高于默认sealed派生 implicit def scalapbEnumTransformer[From <: GeneratedEnum, To <: GeneratedEnum]( implicit toCompanion: GeneratedEnumCompanion[To] ): Transformer[From, To] = (source: From) => toCompanion.fromValue(source.value)
配置完成后直接调用原生Chimney转换语法即可,无歧义报错:
val otherValue: SomeOther = Some.OPTIONA.transformInto[SomeOther]
如果需要安全转换(避免未知value抛异常),可以实现对应PartialTransformer,在转换时做异常捕获,返回Either或者partial.Result类型的转换结果。
方案2:无Chimney依赖的通用转换实现
如果不想引入Chimney依赖,可以修正之前的隐式类逻辑,核心是通过隐式参数自动获取目标枚举的伴生对象,直接基于int值做映射,不需要手动传参:
import scalapb._ implicit class ScalaPbEnumOps[From <: GeneratedEnum](val source: From) extends AnyVal { // 非安全转换:未知value直接抛出异常 def toPbEnum[To <: GeneratedEnum]( implicit companion: GeneratedEnumCompanion[To] ): To = companion.fromValue(source.value) // 安全转换:未知value返回Left def toPbEnumSafe[To <: GeneratedEnum]( implicit companion: GeneratedEnumCompanion[To] ): Either[IllegalArgumentException, To] = scala.util.Try(companion.fromValue(source.value)) .toEither.left.map(e => new IllegalArgumentException( s"值${source.value}无法转换为目标枚举类型", e )) }
使用方式
导入上述隐式后,直接在枚举实例上调用方法即可:
// 非安全转换 val a: SomeOther = Some.OPTIONA.toPbEnum[SomeOther] // 安全转换 val b: Either[IllegalArgumentException, Some] = SomeOther.OPTIONC.toPbEnumSafe[Some]
注意:上述两种方案都是基于枚举的底层int值做映射,不会校验枚举值的语义一致性。如果两个枚举的同value代表的业务含义不同,不要使用通用转换,需要单独编写映射逻辑避免业务错误。
内容的提问来源于stack exchange,提问作者NiceOneMoney
相关产品推荐
相关产品推荐

