如何在Avro4s中将IntEnum字段转换为整数类型
问题描述
在Scala中使用Avro4s序列化样例类时,希望将IntEnum类型的字段在Avro Schema中以整数类型(而非Avro枚举类型)表示。
当前样例类代码:
@AvroName("MyCaseClass") case class MyCaseClass(detectedDeliveryType: DeliveryType, ...) object DeliveryType extends IntEnum[DeliveryType] { case object Unknown extends DeliveryType(0) case object Desktop extends DeliveryType(1) case object MobileWeb extends DeliveryType(2) case object MobileApp extends DeliveryType(3) case object CTV extends DeliveryType(4) override val values: IndexedSeq[DeliveryType] = findValues }
Avro4s默认生成的Schema:
{ "name": "detectedDeliveryType", "type": { "type": "enum", "name": "DeliveryType", "namespace": "mypackage.enums", "symbols": [ "CTV", "Desktop", "MobileApp", "MobileWeb", "Unknown" ] } }
期望生成的Schema:
{ "name": "detectedDeliveryType", "type": "int" }
解决方案
Avro4s基于类型类机制处理Schema生成与序列化/反序列化,只需为IntEnum类型自定义SchemaFor和Codec实例即可实现需求。
方法1:为单个枚举类型定义实例
在DeliveryType的伴生对象中添加以下隐式实例,确保作用域内可用:
import com.sksamuel.avro4s._ import org.apache.avro.Schema object DeliveryType extends IntEnum[DeliveryType] { case object Unknown extends DeliveryType(0) case object Desktop extends DeliveryType(1) case object MobileWeb extends DeliveryType(2) case object MobileApp extends DeliveryType(3) case object CTV extends DeliveryType(4) override val values: IndexedSeq[DeliveryType] = findValues // 自定义Schema生成逻辑,将DeliveryType映射为Avro int类型 implicit val schemaFor: SchemaFor[DeliveryType] = SchemaFor.int // 自定义序列化/反编解码器 implicit val codec: Codec[DeliveryType] = new Codec[DeliveryType] { override def schema: Schema = schemaFor.schema // 序列化:将枚举转为对应的整数值 override def encode(value: DeliveryType): AnyRef = Int.box(value.value) // 反序列化:根据整数查找对应的枚举实例 override def decode(value: Any): DeliveryType = DeliveryType.withValue(value.asInstanceOf[Int]) } }
方法2:通用化处理所有IntEnum类型
如果项目中有多个IntEnum类型需要统一处理为Avro int,可以定义通用的隐式实例,无需为每个枚举重复编写代码:
import com.sksamuel.avro4s._ import enumeratum.{IntEnum, IntEnumEntry} import org.apache.avro.Schema // 通用Schema生成逻辑:所有IntEnumEntry子类映射为Avro int implicit def intEnumSchemaFor[E <: IntEnumEntry]: SchemaFor[E] = SchemaFor.int // 通用编解码器:处理所有IntEnum类型的序列化与反序列化 implicit def intEnumCodec[E <: IntEnumEntry](implicit enum: IntEnum[E]): Codec[E] = new Codec[E] { override def schema: Schema = SchemaFor.int.schema override def encode(value: E): AnyRef = Int.box(value.value) override def decode(value: Any): E = enum.withValue(value.asInstanceOf[Int]) }
验证效果
当生成MyCaseClass的Avro Schema时,detectedDeliveryType字段将被生成为int类型,符合预期。
注意事项
- 确保上述隐式实例在生成Schema、执行序列化/反序列化的代码作用域内可见。
- 反序列化时,若输入的整数不存在于枚举定义中,
withValue会抛出NoSuchElementException,如需容错处理,可改用withValueOpt并添加错误逻辑。
内容的提问来源于stack exchange,提问作者Zvi Mints
相关产品推荐
相关产品推荐

