0

我想介绍一些类型来表示更大类型中字段的可能值。该字段需要能够对 JSON 进行编码/解码,还需要能够写入/读取到数据库。

我还是 Scala 的新手,我想要的类型是 sum type Status = NotVerified | Correct | Wrong。由于我希望有一个与每个构造函数关联的字符串表示,我创建了一个带有String参数的密封案例类,然后创建了扩展该案例类的对象。为了能够编码/解码,我还需要隐式,但我不确定如何构造它。我可以将它们放在对象内部的一个新对象中,如下所示:

sealed case class Status(name: String)
object Status {
  object NotVerified extends Status("not_verified")
  object Correct extends Status("correct")
  object Wrong extends Status("wrong")

  object implicits {
    implicit val encodeStatusJson: Encoder[Status] =
      _.name.asJson
    implicit val decodeStatusJson: Decoder[Status] =
      Decoder.decodeString.map(Status(_))

    implicit val encodeStatus: MappedEncoding[Status, String] =
      MappedEncoding[Status, String](_.name)

    implicit val decodeStatus: MappedEncoding[String, Status] =
      MappedEncoding[String, Status](Status(_))
  }
}

……然后import在需要的地方明确地显示这些,但那是相当……明确的。

组织这种类型+隐式集合的好方法是什么?

4

2 回答 2

1

常见的方法是定义一个sealed trait

sealed trait Status {
  def name: String
}

object Status {
  case object NotVerified extends Status {
    val name = "not_verified"
  }
  case object Correct extends Status {
    val name = "correct"
  }
  case object Wrong extends Status {
    val name = "wrong"
  }
}

或者 a sealed abstract class,在当前的 Scala 版本中可能看起来更好:

sealed abstract class Status(val name: String)

object Status {
  case object NotVerified extends Status("not_verified")
  case object Correct extends Status("correct")
  case object Wrong extends Status("wrong")
}

为了避免需要导入隐式,它们可以直接放在类型的伴生对象中。另请参阅问题Scala 在哪里寻找隐式?有关更多详细信息,尤其是Type Companion Objects部分。

是的,为这样的枚举定义隐式很容易重复。您必须求助于反射或宏。我推荐使用Enumeratum库,它还集成了 Circe 和 Quill。以下是 Circe 的示例:

import enumeratum.values._

sealed abstract class Status(val value: String) extends StringEnumEntry {
  def name: String = value
}

object Status extends StringEnum[Status] with StringCirceEnum[Status] {
  val values = findValues

  case object NotVerified extends Status("not_verified")
  case object Correct extends Status("correct")
  case object Wrong extends Status("wrong")
}

您可以在不明确定义任何编码器/解码器或从以下位置导入任何内容的情况下使用它Status

scala> import io.circe.syntax._

scala> val status: Status = Status.Correct
status: Status = Correct

scala> status.asJson
res1: io.circe.Json = "correct"

scala> Decoder[Status].decodeJson(Json.fromString("correct"))
res2: io.circe.Decoder.Result[Status] = Right(Correct)
于 2018-11-28T15:28:47.667 回答
0

如果您添加一个apply方法,您可以Status从 a创建适当的方法String,这应该可以Decoder正常工作。并使Status抽象

sealed abstract class Status(name: String)

object Status {
  object NotVerified extends Status("not_verified")
  object Correct extends Status("correct")
  object Wrong extends Status("wrong")

  def apply(name: String): Status = name match {
    case "not_verified" => NotVerified
    case "correct" => Correct
    case _ => Wrong
  }
}

我认为你现有的隐式仍然可以工作,但我不知道那些特定的库......

于 2018-11-28T15:28:15.907 回答