Play JSON 中有没有办法为不是对象(/数组)的东西定义 reader?

Is there a way in Play JSON to define a reader for something that is not an object (/array)?

基本上,我有一个JSON这个形状:

{
  "color": "#abcdef"
}

所以我写了一个Reads:

import java.awt.Color

case class Options(color: Color)

((__ \ "color").read[Color])(Options _)

除了 Color 没有 reader。我的第二次尝试是:

(
  Color.decode((__ \ "color").read[String])
)(Options _)

但这显然也不正确。该文档显示了为 { }(和 [ ])创建 reader 的方法,但不是为数字或字符串等“基元”创建的方法。我可以这样做吗?

你可以这样做:

case class Options(color: Color)

object Options {
  implicit val colorReads: Reads[Color] = __.read[String].map(Color.decode)
  implicit val optionsReads: Reads[Options] = Json.reads[Options]
}

那么用法是:

val b = Json.parse("{ \"color\": \"#abcdef\"}").as[Options]
println(b) // prints: Options(java.awt.Color[r=171,g=205,b=239])

您需要为 Color 定义 Reads 以与 Play Json 基础架构的其余部分保持一致,例如:

import scala.util._
import play.api.libs.json._
import java.awt.Color

case class Options(color: Color)

object Options {
  implicit val colorReads: Reads[Color] = {
    implicitly[Reads[String]].flatMapResult { value =>
      def invalidColor(cause: Throwable) = {
        JsError(JsonValidationError(s"Invalid color value: `$value`. Error: ${cause.getMessage}"))
      }

      def validColor(color: Color) = {
        JsSuccess(color)
      }
      Try(Color.decode(value)).fold(invalidColor, validColor)
    }
  }

  implicit val reads: Reads[Options] = Json.reads
}

val jsonString = """ { "color": "#abcdef" }""".stripMargin
/*
 * Prints out: Options(java.awt.Color[r=171,g=205,b=239])
 */
println(Json.parse(jsonString).as[Options])

val invalidColorJsonString = """ { "color": "invalid_color" }""".stripMargin

/*
 * Prints out: JsError(List((/color,List(JsonValidationError(List(Invalid color value: `invalid_color`. Error: For input string: "invalid_color"),WrappedArray()))))) 
 */
println(Json.parse(invalidColorJsonString).validate[Options])

使用的 scala:2.12,sbt:1.4.7,Play Json:2.9.2