1

Basically, I have a JSON of this shape:

{
  "color": "#abcdef"
}

so I write a Reads:

import java.awt.Color

case class Options(color: Color)

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

except there is no reader for Color. My second attempt was:

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

but that's also apparently not correct. The documentation show ways to create readers for { } (and [ ])'s, but not for "primitives" like numbers or strings. Can I do that?

cchantep
  • 9,118
  • 3
  • 30
  • 41
RubenVerg
  • 145
  • 1
  • 8

2 Answers2

1

You can do:

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]
}

Then the usage is:

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

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
1

You need to defined Reads for Color to be consistent with rest of Play Json infrastructure, like:

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])

Used scala: 2.12, sbt: 1.4.7, Play Json: 2.9.2

Ivan Kurchenko
  • 4,043
  • 1
  • 11
  • 28
  • This looks way more complicated than it should be – RubenVerg Mar 08 '21 at 14:19
  • @RubenVerg it looks like this, because of proper error handling. – Ivan Kurchenko Mar 08 '21 at 14:19
  • Doesn't `Color.decode("sdahd")` throwing already handle correctly? – RubenVerg Mar 08 '21 at 14:22
  • If to remove `Try` block during decoding invalid color exception will be re-thrown instead of `JsError` result returned. – Ivan Kurchenko Mar 08 '21 at 14:25
  • @RubenVerg I removed `Try` and all logic and see as the result: `Options(java.awt.Color[r=171,g=205,b=239]) Exception in thread "main" java.lang.NumberFormatException: For input string: "invalid_color" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)` – Ivan Kurchenko Mar 08 '21 at 14:26
  • ok. crashing works just fine in my use-case, but thanks anyways for showing advanced error handling stuff – RubenVerg Mar 08 '21 at 14:28
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/229658/discussion-between-ivan-kurchenko-and-rubenverg). – Ivan Kurchenko Mar 08 '21 at 17:41