在Scala with Play and Slick中,我将enum Country实现为案例类,并希望将其作为字符串读/写为JSON。这是完整的代码:
package models.enums
import play.api.libs.json._
import slick.lifted.MappedTo
case class Country(id: String) extends MappedTo[String] {
override def value: String = id
}
object Country {
val France = Country("FR")
val Germany = Country("DE")
implicit val writes = Writes[Country](
(site: Country) => JsString(site.value)
)
// this line I need help with
implicit val reads = Json.reads[Country]
}
国家枚举用于其他模型,例如
{
"name": "Robo"
"country": "DE"
}
上面的代码将枚举写为一个普通的字符串,这是我想要的,但我无法弄清楚如何实现隐式读取部分。目前只有在JSON如下的情况下才能读取:
{
"name": "Robo"
"country": {
"id": "DE"
}
}
我确信答案很简单,只是无法弄清楚正确的语法。
答案 0 :(得分:1)
import play.api.libs.json._
import play.api.libs.json.Reads._
implicit val reads = new Reads[Country] {
override def reads(json: JsValue): JsResult[Country] = for {
id <- (json \ "country").validate[String]
} yield Country(id)
}
(json \ "country").validate[String]
返回JsResult[String]
,然后将其yield
映射到JsResult[Country]
或强>
import play.api.libs.json._
import play.api.libs.json.Reads._
implicit val reads = new Reads[Country] {
override def reads(json: JsValue): JsResult[Country] =
(json \ "country").validate[String].map(Country(_))
或强>
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
implicit val reads: Reads[Country] = (__ \ "country").reads[String].map(Country(_))
修改强>
关于你对验证的评论,我只是将它添加到你的case类,而不是json解析器:
case class Country(id: String) extends MappedTo[String] {
private val validIds = Set(France, Germany).map(_.id)
require(validIds contains id, s"id must be one of $validIds")
override def value: String = id
}
诀窍是你放在类体中的语句将成为构造函数的一部分。如果有人试图创建一个ID不在您允许的ID列表中的require
,则Country
调用会抛出异常。
答案 1 :(得分:0)
我建议使用密封类。如果模式匹配的选择器是密封类的实例,则模式匹配的编译可以发出警告,该警告诊断给定的一组模式不是详尽的,即可能会MatchError
被提升在运行时。
我的项目示例:
import com.pellucid.sealerate
import play.api.libs.json._
sealed abstract class TokenKind(val value: String)
object TokenKind {
case object PasswordReset extends TokenKind("passwordReset")
case object AccountConfirmation extends TokenKind("accountConfirmation")
def values: Set[TokenKind] = sealerate.values[TokenKind]
def apply(value: String): TokenKind = values.find(_.value == value).getOrElse(throw new RuntimeException(s"Can't construct TokenKind from: $value"))
implicit def tokenKindWrites = new Writes[TokenKind] {
override def writes(o: TokenKind): JsValue = JsString(o.value)
}
implicit def tokenKindReads = new Reads[TokenKind] {
override def reads(json: JsValue): JsResult[TokenKind] = json match {
case JsString(string) => JsSuccess(TokenKind(string))
case _ => JsError("validate.error.invalidTokenKind")
}
}
}
答案 2 :(得分:0)
我有类似的问题解决了这个问题:
object VoteType extends Enumeration {
val Up, Down = Value
implicit val voteTypeMappeer = MappedColumnType.base[VoteType.Value, String](_.toString, VoteType.withName)
}