如何在Play Scala中将字符串作为枚举类读取?

时间:2016-03-24 09:11:52

标签: scala playframework slick

在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"
  }
}

我确信答案很简单,只是无法弄清楚正确的语法。

3 个答案:

答案 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)
}