如何在 Scala 中将特征序列化和反序列化到 Json 或从 Json 反序列化?

How to serialize and deserialize traits, to and from Json, in Scala?

第一次尝试:

到目前为止我已经尝试过喷雾-json。我有:


trait Base

case class A ( id: String) extends Base

case class B (id: String) extends Base

现在,为了序列化和反序列化我的 Base 类型,我有代码:

implicit object BaseFormat extends RootJsonFormat[Base]{
    def write(obj: Base): JsValue = {
      obj match {
        case a: A => a.toJson
        case b: B => b.toJson
        case unknown @ _ => serializationError(s"Marshalling issue with ${unknown}")
      }
    }

    def read(json: JsValue): Base = {
      //how to know whether json is encoding an A or a B?
    }
  }

问题是,为了实现反序列化的 read 方法,我想不出一种方法来知道 JsValue 编码的是 A 还是 B .

第二次尝试:

为了在 spray-json 中解决这个问题,我最终只是将 A 中的字段 id 重命名为 aID,并将 B 中的字段重命名为 bID.

第三次尝试:

由于 spray-json 不如 zio-jsoncirce 等替代库复杂,它们无需额外代码即可自行处理此问题,因此我开始使用 zio-json

现在我得到了错误

magnolia: could not infer DeriveJsonEncoder.Typeclass for type

对于所有情况 类 采用类型参数。 此外,它在链式特征继承方面存在问题。似乎 circe 也使用木兰。所以这很可能也会被 circe 复制。

如有任何帮助,我们将不胜感激。

您应该使用 json encoding/decoding 库解决这个问题。 这是一个使用 circe 的例子,它是半自动模式。

既然你在评论中提到你在你的案例中遇到泛型类型问题 classes,我也把它包括在内。基本上,要为包含 T 的 class Foo[T] 导出编码器或解码器,您必须证明存在编码和解码 T 的方法。这是通过请求一个隐式的 Encoder[T]Decoder[T] 来完成的,您可以在其中导出 Encoder[Foo[T]]Decoder[Foo[T]]。当然,您可以将此推理概括为适用于多个泛型类型,您只需要隐含地使用一对 encoder/decoder,在其中为相应的情况 class 派生 encoder/decoder。

import io.circe._, io.circe.generic.semiauto._, io.circe.syntax._

case class Foo[T](a: Int, b: String, t: T)
object Foo {
  implicit def decoder[T: Encoder: Decoder]: Decoder[Foo[T]] = deriveDecoder
  implicit def encoder[T: Encoder: Decoder]: Encoder[Foo[T]] = deriveEncoder
}

case class Bar(a: Int)
object Bar {
  implicit val encoder: Encoder[Bar] = deriveEncoder
  implicit val decoder: Decoder[Bar] = deriveDecoder
}

case class Baz(a: Int)

println(Foo(42, "hello", 23.4).asJson) // Works because circe knows Encoder[Float] 
println(Foo(42, "hello", Bar(42)).asJson) // Works because we defined Encoder[Bar] 

//println(Foo(42, "hello", Baz(42)).asJson) // Doesn't compile: circe doesn't know Encoder[Baz] in semi-auto mode

Try it live on Scastie

请注意,需要为您使用的每种类型 T 生成 Foo[T] 的不同 encoder/decoder,这就是 [=21= 的编码器和解码器派生的原因] 必须是方法,而不是像 Bar.

这样的值

还有一个全自动模式,但它往往会产生编译时错误,对于初学者来说更难调试,所以我会从半自动开始。另一个问题是,自动模式在大型项目上编译可能需要更长的时间。如果你喜欢冒险,不犯错就更美了!

import io.circe._, io.circe.generic.auto._, io.circe.syntax._

case class Foo[T](a: Int, b: String, t: T)
case class Bar(a: Int)
case class Baz(a: Int)

println(Foo(42, "hello", 23.4).asJson) // circe knows Encoder[Float] and automatically derives Encoder[Foo[Float]]
println(Foo(42, "hello", Bar(42)).asJson) // circe wants Encoder[Foo[Bar]], so it ma(cro)gically derives Encoder[Bar] and then Encoder[Foo[Bar]]
println(Foo(42, "hello", Baz(42)).asJson) // Does compile this time, circe automatically find the encoder/decoder for Baz the same way it does for Bar

Try it live on Scastie

一个有效的 example 将 traits 与泛型结合使用是这样的:

import io.circe.Decoder.Result
import io.circe._
import io.circe.generic.semiauto._
import io.circe.syntax._


trait MyBase[T <: MyBase[T]] {
  def myid: String
  def issue: MyBaseIssue
  def summary: MyBaseSummary[T]
}

trait MyBaseIssue

object MyBaseIssue {
  implicit val decodeIssue: Decoder[MyBaseIssue] =
    Decoder[SimpleBaseIssue].map[MyBaseIssue](identity).or(
      Decoder[SophisticatedBaseIssue].map[MyBaseIssue](identity)
    )

  implicit val encodeIssue: Encoder[MyBaseIssue] = Encoder.instance {
    case simple @ SimpleBaseIssue(_) => simple.asJson
    case sophisticated @ SophisticatedBaseIssue(_) => sophisticated.asJson

  }
}

trait MyBaseSummary[T <: MyBase[T]]

object MyBaseSummary {
  implicit def decodeSummary[T <: MyBase[T]: Encoder: Decoder]
  : Decoder[MyBaseSummary[T]] =
    Decoder[SimpleBaseSummary].map[MyBaseSummary[SimpleBase]](identity).asInstanceOf[Decoder[MyBaseSummary[T]]].or(
      Decoder[SophisticatedBaseSummary].map[MyBaseSummary[SophisticatedBase]](identity).asInstanceOf[Decoder[MyBaseSummary[T]]]
    )


  implicit def encodeSummary[T <: MyBase[T]: Encoder: Decoder]
  : Encoder[MyBaseSummary[T]] = Encoder.instance {
    case simple @ SimpleBaseSummary(_) => simple.asJson
    case sophisticated @ SophisticatedBaseSummary(_) => sophisticated.asJson

  }
}

case class SimpleBase(
                       myid: String,
                       override val issue: SimpleBaseIssue,
                       override val summary: SimpleBaseSummary
                     ) extends MyBase[SimpleBase]

case class SophisticatedBase(
                              myid: String,
                              extraField: String,
                              override val issue: SophisticatedBaseIssue,
                              override val summary: SophisticatedBaseSummary
                            ) extends MyBase[SophisticatedBase]

object SimpleBase {
  implicit val encoder: Encoder[SimpleBase] = deriveEncoder
  implicit val decoder: Decoder[SimpleBase] = deriveDecoder
}

object SophisticatedBase {
  implicit val encoder: Encoder[SophisticatedBase] = deriveEncoder
  implicit val decoder: Decoder[SophisticatedBase] = deriveDecoder
}

case class SimpleBaseIssue(simpleIssueType: String) extends MyBaseIssue
case class SophisticatedBaseIssue(sophisticatedIssueType: String) extends MyBaseIssue

object SimpleBaseIssue {
  implicit val encoder: Encoder[SimpleBaseIssue] = deriveEncoder
  implicit val decoder: Decoder[SimpleBaseIssue] = deriveDecoder
}

object SophisticatedBaseIssue {
  implicit val encoder: Encoder[SophisticatedBaseIssue] = deriveEncoder
  implicit val decoder: Decoder[SophisticatedBaseIssue] = deriveDecoder
}

case class SimpleBaseSummary(simpleSummary: String) extends MyBaseSummary[SimpleBase]

case class SophisticatedBaseSummary(sophisticatedSummary: String) extends MyBaseSummary[SophisticatedBase]

object SimpleBaseSummary {
  implicit val encoder: Encoder[SimpleBaseSummary] = deriveEncoder
  implicit val decoder: Decoder[SimpleBaseSummary] = deriveDecoder
}

object SophisticatedBaseSummary {
  implicit val encoder: Encoder[SophisticatedBaseSummary] = deriveEncoder
  implicit val decoder: Decoder[SophisticatedBaseSummary] = deriveDecoder
}

case class MyBaseList[T <: MyBase[T]](
                                       myid: String,
                                       var membersMap: Map[String, T] = Map.empty,
                                       override val issue: MyBaseListIssues[T],
                                       override val summary: MyBaseListSummary[T]
                                     ) extends MyBase[MyBaseList[T]]

object MyBaseList {

  implicit def membersMapDecoder[T <: MyBase[T]: Encoder: Decoder]
  : Decoder[Map[String, T]] = Decoder.decodeMap
  implicit def membersMapEncoder[T <: MyBase[T]: Encoder: Decoder]
  : Encoder[Map[String, T]] = Encoder.encodeMap


  implicit def encoder[T <: MyBase[T]: Encoder: Decoder]
  : Encoder[MyBaseList[T]] = deriveEncoder
  implicit def decoder[T <: MyBase[T]: Encoder: Decoder]
  : Decoder[MyBaseList[T]] = deriveDecoder
}

case class MyBaseListIssues[T <: MyBase[T]](
                                             issue: String,
                                             var set: Set[MyBaseIssue] = Set[MyBaseIssue]()
                                           ) extends MyBaseIssue

object MyBaseListIssues {
  implicit def membersMapDecoder: Decoder[Set[MyBaseIssue]] =
    Decoder.decodeSet[MyBaseIssue]
  implicit def membersMapEncoder: Encoder[Set[MyBaseIssue]] = Encoder.encodeSet

  implicit def encoder[T <: MyBase[T]: Encoder: Decoder]
  : Encoder[MyBaseListIssues[T]] = deriveEncoder
  implicit def decoder[T <: MyBase[T]: Encoder: Decoder]
  : Decoder[MyBaseListIssues[T]] = deriveDecoder
}

case class MyBaseListSummary[T <: MyBase[T]](
                                              summaryList: Map[String, MyBaseSummary[T]]
                                            ) extends MyBaseSummary[MyBaseList[T]]

object MyBaseListSummary {

  implicit def membersMapDecoder[T <: MyBase[T]: Encoder: Decoder]
  : Decoder[Map[String, MyBaseSummary[T]]] = Decoder.decodeMap
  implicit def membersMapEncoder[T <: MyBase[T]: Encoder: Decoder]
  : Encoder[Map[String, MyBaseSummary[T]]] = Encoder.encodeMap


  implicit def encoder[T <: MyBase[T]: Encoder: Decoder]
  : Encoder[MyBaseListSummary[T]] = deriveEncoder
  implicit def decoder[T <: MyBase[T]: Encoder: Decoder]
  : Decoder[MyBaseListSummary[T]] = deriveDecoder
}



object MainObject extends App {
  val simpleList = MyBaseList[SimpleBase]("simpleId",
    Map("one" -> SimpleBase("baseid", SimpleBaseIssue("the mistake"), SimpleBaseSummary("very concise"))),
    MyBaseListIssues[SimpleBase]("listIssue", Set(SimpleBaseIssue("a disaster"))),
    MyBaseListSummary[SimpleBase]( Map("simplebaseid" -> SimpleBaseSummary("super concise")))
  )
  val simpleJson = simpleList.asJson
  println(simpleJson)
  val convertedSimpleList = simpleJson.as[MyBaseList[SimpleBase]]
  println(convertedSimpleList)


  val sphisticatedList = MyBaseList[SophisticatedBase]("sophisticatedId",
    Map("one" -> SophisticatedBase("baseid", "further detail", SophisticatedBaseIssue("the mistake"), SophisticatedBaseSummary("very concise"))),
    MyBaseListIssues[SophisticatedBase]("listIssue", Set(SophisticatedBaseIssue("a disaster"))),
    MyBaseListSummary[SophisticatedBase]( Map("sophisticatedbaseid" -> SophisticatedBaseSummary("super concise")))
  )
  val sophisticatedJson = sphisticatedList.asJson

  println(sophisticatedJson)

  val convertedSophisticatedListDecoder: Result[MyBaseList[SophisticatedBase]] = sophisticatedJson.as[MyBaseList[SophisticatedBase]]

  println(convertedSophisticatedListDecoder)

  convertedSophisticatedListDecoder match {
    case Left(failure) => println(failure)
    case Right(list) => println(list)
  }
}


仍然必须确保每个继承特定特征的案例 class 与继承相同特征的其他案例具有不同数量的字段,或者至少与其他案例不同 classes 在这些字段的命名中。

否则,解码器将无法正确完成其工作并将 json 解码为在解码器函数中匹配它的第一种情况 class,而不是正确的预期情况。