您可以为每个案例类定义编码器/解码器(编解码器),因为val
它不会每次都创建:
import io.circe.generic.semiauto.deriveCodec
import io.circe.Codec
private implicit val dockerCodec: Codec.AsObject[DockerGroup] = deriveCodec[DockerGroup]
private implicit val bowerCodec: Codec.AsObject[BowerGroup] = deriveCodec[BowerGroup]
private implicit val mvnCodec: Codec.AsObject[MavenGroup] = deriveCodec[MavenGroup]
方法1:类似于编解码器的结构
我认为没有办法做这样的通用编解码器,isInstanceOf
但我会弄错。
所以,我会为一些T
使用具体编解码器的编解码器定义取决于type
字段:
import io.circe.Decoder.Result
import io.circe.generic.semiauto.deriveCodec
import io.circe.{Codec, HCursor, Json}
import io.circe.syntax._
object Codecs {
private implicit val dockerCodec: Codec.AsObject[DockerGroup] = deriveCodec[DockerGroup]
private implicit val bowerCodec: Codec.AsObject[BowerGroup] = deriveCodec[BowerGroup]
private implicit val mvnCodec: Codec.AsObject[MavenGroup] = deriveCodec[MavenGroup]
implicit def codec[T <: GroupRepository]: Codec[T] = new Codec[T] {
override def apply(a: T): Json =
(a match {
case d: DockerGroup => d.asInstanceOf[DockerGroup].asJsonObject
case b: BowerGroup => b.asInstanceOf[BowerGroup].asJsonObject
case m: MavenGroup => m.asInstanceOf[MavenGroup].asJsonObject
}).+:("type", a.`type`.asJson).asJson
override def apply(c: HCursor): Result[T] = c.downField("type").as[String].flatMap {
case "docker" => c.as[DockerGroup]
case "bower" => c.as[BowerGroup]
case "maven" => c.as[MavenGroup]
}.map(_.asInstanceOf[T])
}
}
object Test extends App {
import Codecs._
println(DockerGroup("doc", Seq("a", "b")).asJson)
println(DockerGroup("doc", Seq("a", "b")).asJson.as[DockerGroup])
println(BowerGroup("bow", Seq("a", "b")).asJson)
println(BowerGroup("bow", Seq("a", "b")).asJson.as[BowerGroup])
println(MavenGroup("mvn", Seq("a", "b")).asJson)
println(MavenGroup("mvn", Seq("a", "b")).asJson.as[MavenGroup])
}
输出:
{
"type" : "docker",
"name" : "doc",
"repositories" : [
"a",
"b"
],
"blobstore" : "default"
}
Right(DockerGroup(doc,List(a, b),default))
{
"type" : "bower",
"name" : "bow",
"repositories" : [
"a",
"b"
],
"blobstore" : "default"
}
Right(BowerGroup(bow,List(a, b),default))
{
"type" : "maven",
"name" : "mvn",
"repositories" : [
"a",
"b"
],
"blobstore" : "default"
}
Right(MavenGroup(mvn,List(a, b),default))
方法2:摆脱type
字段并使用可配置派生
我们可以在我们的解析 JSON中定义鉴别器字段,并在父级中去掉这个字段(它现在可以是一个特征)。在这里,我使用一些临时鉴别器来获得结果 json 中字段的类似结果,但我认为在这种方法中它可能更优雅:type
Configuration
abstract class GroupRepository
type
constructorName => constructorName.toLowerCase.dropRight("Group".length)
请记住,要使用ConfiguredJsonCodec
注释,您应该implicit val config: Configuration
在伴随对象中定义。此外,您应该-Ymacro-annotations
为宏的 scala 编译器添加标志:
scalacOptions ++= Seq("-Ymacro-annotations")
完整代码:
import io.circe.Decoder.Result
import io.circe.generic.extras.{Configuration, ConfiguredJsonCodec}
import io.circe.syntax._
import io.circe.{Codec, HCursor, Json}
import ru.hardmet.GroupRepository._
@ConfiguredJsonCodec
sealed trait GroupRepository {
def name: String
def repositories: Seq[String]
def blobstore: String
}
case class DockerGroup(name: String, repositories: Seq[String], blobstore: String = "default")
extends GroupRepository
case class BowerGroup(name: String, repositories: Seq[String], blobstore: String = "default")
extends GroupRepository
case class MavenGroup(name: String, repositories: Seq[String], blobstore: String = "default")
extends GroupRepository
object GroupRepository {
implicit val config: Configuration =
Configuration.default
.withDiscriminator("type").copy(
transformConstructorNames = _.toLowerCase.dropRight("Group".length)
)
}
object GroupRepositoryCodec {
implicit def codec[T <: GroupRepository]: Codec[T] = new Codec[T] {
override def apply(a: T): Json = a.asInstanceOf[GroupRepository].asJson
override def apply(c: HCursor): Result[T] = c.as[GroupRepository].map(_.asInstanceOf[T])
}
}
object JsonExperiments extends App {
import GroupRepositoryCodec._
println(DockerGroup("doc", Seq("a", "b")).asJson)
println(DockerGroup("doc", Seq("a", "b")).asJson.as[DockerGroup])
println(BowerGroup("bow", Seq("a", "b")).asJson)
println(BowerGroup("bow", Seq("a", "b")).asJson.as[BowerGroup])
println(MavenGroup("mvn", Seq("a", "b")).asJson)
println(MavenGroup("mvn", Seq("a", "b")).asJson.as[MavenGroup])
}
输出将相同但有所不同 - type
JSON 中的字段位于对象的末尾:
{
"name" : "doc",
"repositories" : [
"a",
"b"
],
"blobstore" : "default",
"type" : "docker"
}