Circe Scala - 编码和解码 Map[] 和案例类
Posted
技术标签:
【中文标题】Circe Scala - 编码和解码 Map[] 和案例类【英文标题】:Circe Scala - Encode & Decode Map[] and case classes 【发布时间】:2019-03-13 10:46:49 【问题描述】:我正在尝试为我拥有的案例类创建编码器和解码器:
case class Road(id: String, light: RoadLight, names: Map[String, String])
RoadLight 是一个带有枚举的 java 类。
public enum RoadLight
red,yellow,green
我尝试进行半自动编码和解码:制作隐式编码器和解码器。 我从 Map[String,String] 类型开始:
implicit val namesDecoder: Decoder[Map[String, String]] = deriveDecoder[Map[String, String]]
implicit val namesEncoder: Encoder[Map[String, String]] = deriveEncoder[Map[String, String]]
但我确实得到了他们两个的错误!
1: 找不到 io.circe.generic.decoding.DerivedDecoder[A] 类型的惰性隐式值
2: 错误:没有足够的参数用于方法 derivedDecoder: (implicit decode: shapeless.Lazy[io.circe.generic.decoding.DerivedDecoder[A]])io.circe.Decoder[A ]。 未指定值参数解码。 隐式验证名称解码器:解码器[地图[字符串,字符串]]=派生解码器
我已经按照书本做了所有事情,无法理解哪里出了问题。我什至没有尝试解析案例类,只有地图,即使这样也行不通。
有什么想法吗?谢谢!
【问题讨论】:
【参考方案1】:Scaladoc 说
/**
* Semi-automatic codec derivation.
*
* This object provides helpers for creating [[io.circe.Decoder]] and [[io.circe.ObjectEncoder]]
* instances for case classes, "incomplete" case classes, sealed trait hierarchies, etc.
Map
不是密封特征层次结构的案例类或元素。
https://github.com/circe/circe/issues/216
Encode Map[String, MyCaseClass] into Seq[String, String] using circe
Circe and Scala's Enumeration type
【讨论】:
【参考方案2】:circe-generic
不会为 java 枚举创建编解码器,仅适用于 scala 乘积和总和类型。但是为RoadLight
滚动你自己并不难。一旦你有了它,你就会得到地图。
下面的代码有效:
object RoadLightCodecs
implicit val decRl: Decoder[RoadLight] = Decoder.decodeString.emap
case "red" => Right(RoadLight.Red)
case "yellow" => Right(RoadLight.Yellow)
case "green" => Right(RoadLight.Green)
case s => Left(s"Unrecognised traffic light $s")
implicit val encRl: Encoder[RoadLight] = Encoder.encodeString.contramap(_.toString)
implicit val decodeMap = Decoder.decodeMap[String, RoadLight]
implicit val encodeMap = Encoder.encodeMap[String, RoadLight]
所以我们所做的是为基本类型制作编解码器,然后使用它们来构建更大的地图编解码器。
现在据我所知,没有任何库可以为 java 枚举自动执行此操作,尽管理论上应该可以编写一个。但是在基本编解码器上使用组合器来构建更复杂的编解码器效果很好并且可以很好地扩展。
编辑:我玩过自动派生 java 枚举编解码器,你可以几乎做到这一点:
def decodeEnum[E <: Enum[E]](values: Array[E]): Decoder[E] = Decoder.decodeString.emap str =>
values.find(_.toString.toLowerCase == str)
.fold[Either[String, E]](Left(s"Value $str does not map correctly"))(Right(_))
def encodeEnum[E <: Enum[E]]: Encoder[E] =
Encoder.encodeString.contramap(_.toString.toLowerCase)
implicit val roadLightDecoder = decodeEnum[RoadLight](RoadLight.values())
implicit val roadLightEncoder = encodeEnum[RoadLight]
所以 encodeEnum 可以是自动的(你可以让它隐含而不是最后的 val)但是需要给解码器提供值(我认为没有办法从类型中自动获取),所以你需要通过创建编解码器时的那些。
【讨论】:
以上是关于Circe Scala - 编码和解码 Map[] 和案例类的主要内容,如果未能解决你的问题,请参考以下文章
用于编码/解码 arity 0 的密封特征实例的 Circe 实例?