我試圖找到一種簡單有效的方法來(反)序列化 Scala 3 中的列舉,使用circe.
考慮以下示例:
import io.circe.generic.auto._
import io.circe.syntax._
enum OrderType:
case BUY
case SELL
case class Order(id: Int, `type`: OrderType, amount: String)
val order = Order(1, OrderType.SELL, "123.4")
order.asJson
在序列化資料時,它變成
{
"id" : 1,
"type" : {
"SELL" : {
}
},
"amount" : "123.4"
}
代替
{
"id" : 1,
"type" : "SELL",
"amount" : "123.4"
}
這就是我想要的。
我知道我可以為此撰寫一個自定義(反)序列化程式,這將解決這個特定實體的問題,如下所示:
implicit val encodeOrderType: Encoder[OrderType] = (a: OrderType) =>
Encoder.encodeString(a.toString)
implicit def decodeOrderType: Decoder[OrderType] = (c: HCursor) => for {
v <- c.as[String]
} yield OrderType.valueOf(v)
但我一直在尋找一種可能適用于任何enum.
編輯 1
進行序列化的一種方法,(反序列化不起作用:/)是使所有列舉擴展一個公共特征并為所有擴展它的列舉定義編碼器。對于上面的示例,它看起來像這樣。
trait EnumSerialization
enum OrderType extends EnumSerialization:
case BUY
case SELL
enum MagicType extends EnumSerialization:
case FIRE
case WATER
case EARTH
case WIND
implicit def encodeOrderType[A <: EnumSerialization]: Encoder[A] = (a: A) => Encoder.encodeString(a.toString)
// This correctly serializes all instances of enum into a string
case class Order(id: Int, `type`: OrderType, amount: String)
val order = Order(1, OrderType.SELL, "123.4")
val orderJson = order.asJson
// Serializes to { "id" : 1, "type" : "SELL", "amount" : "123.4"}
case class Magic(id: Int, magic: MagicType)
val magic = Magic(3, MagicType.WIND)
val magicJson = magic.asJson
// Serializes to { "id" : 3, "magic" : "WIND"}
然而,這并沒有擴展到反序列化。
uj5u.com熱心網友回復:
在 Scala 3 中,您可以使用 Mirrors 直接進行推導:
import io.circe._
import scala.compiletime.summonAll
import scala.deriving.Mirror
inline def stringEnumDecoder[T](using m: Mirror.SumOf[T]): Decoder[T] =
val elemInstances = summonAll[Tuple.Map[m.MirroredElemTypes, ValueOf]]
.productIterator.asInstanceOf[Iterator[ValueOf[T]]].map(_.value)
val elemNames = summonAll[Tuple.Map[m.MirroredElemLabels, ValueOf]]
.productIterator.asInstanceOf[Iterator[ValueOf[String]]].map(_.value)
val mapping = (elemNames zip elemInstances).toMap
Decoder[String].emap { name =>
mapping.get(name).fold(Left(s"Name $name is invalid value"))(Right(_))
}
inline def stringEnumEncoder[T](using m: Mirror.SumOf[T]): Encoder[T] =
val elemInstances = summonAll[Tuple.Map[m.MirroredElemTypes, ValueOf]]
.productIterator.asInstanceOf[Iterator[ValueOf[T]]].map(_.value)
val elemNames = summonAll[Tuple.Map[m.MirroredElemLabels, ValueOf]]
.productIterator.asInstanceOf[Iterator[ValueOf[String]]].map(_.value)
val mapping = (elemInstances zip elemNames).toMap
Encoder[String].contramap[T](mapping.apply)
enum OrderType:
case BUY
case SELL
object OrderType:
given decoder: Decoder[OrderType] = stringEnumDecoder[OrderType]
given encoder: Encoder[OrderType] = stringEnumEncoder[OrderType]
end OrderType
import io.circe.syntax._
import io.circe.generic.auto._
case class Order(id: Int, `type`: OrderType, amount: String)
val order = Order(1, OrderType.SELL, "123.4")
order.asJson
// {
// "id" : 1,
// "type" : "SELL",
// "amount" : "123.4"
// }: io.circe.Json
它使用inline和Mirror
- 獲取 sum 型別元素的型別串列
- 獲取這些型別的標簽串列
- 獲得他們的價值來創造
Map[String, T]或Map[T, String] map/contramapString編解碼器,以便翻譯是嵌套的,也不需要區分欄位
它僅適用于由 s 組成的列舉,case object并且如果任何存盤了一些嵌套資料,它將失敗case- 為此使用具有鑒別器欄位或以嵌套型別命名的嵌套結構的標準派生程序。
您可以匯入stringEnumDecoder并stringEnumEncoder使其給出,盡管我更愿意手動添加它們,因為它們更像是一個例外而不是規則。
uj5u.com熱心網友回復:
有一個庫可以幫助您:circe-tagged-adt-codec-scala3
有了這個你的代碼看起來像
import org.latestbit.circe.adt.codec._
enum OrderType derives JsonTaggedAdt.Encoder:
case BUY
case SELL
enum MagicType derives JsonTaggedAdt.Encoder:
case FIRE
case WATER
case EARTH
case WIND
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/419710.html
標籤:
