Warm tip: This article is reproduced from stackoverflow.com, please click
json playframework scala

Parsing "stringified" JSON in scala/play

发布于 2020-04-23 11:45:25

I'm implementing a Scala/Play API (apiA) which consumes an API (apiB) that I have no control over. ApiB returns a JSON response which in some cases has JSON embedded into strings. Example:

{
  "name":"some_name",
  "scores": "[[10,15]]",
  "data": "{\"attr1\":\"value1\",\"attr2\":\"value3\"}"
}

ApiA needs to access the value of name before passing all the data along to the client and it should be provided to the client as proper JSON. I'm considering parsing the raw response into a

case class Response(name: String, scores: JsValue, data: JsValue)

or

case class Response(name: String, scores: Seq[Seq[Int]], data: Map[String, String])

For now it doesn't really matter what type scores and data are parsed into as long as it's not a string containing JSON.

Now, if the JSON response was well formed writing format/reads/writes would be straightforward but I'm sort of breaking my head on how to got about converting the content into JSON before parsing into the final types.

Any help would be appreciated.

Questioner
ndx
Viewed
16
cchantep 2020-02-08 20:52
case class Response(name: String, scores: Seq[Seq[Int]], data: Map[String, String])

import play.api.libs.json._

val stringified = Reads[JsValue] {
  _.validate[String].flatMap { raw =>
    try {
      JsSuccess(Json.parse(raw))
    } catch {
      case cause =>
        JsError(cause.getMessage)
    }
  }
}

implicit val respReads = Reads[Response] { js =>
  for {
    name <- (js \ "name").validate[String]
    scores <- (js \ "scores").validate(
      stringified).flatMap(_.validate[Seq[Seq[Int]]])
    data <- (js \ "data").validate(
      stringified).flatMap(_.validate[Map[String, String]])
  } yield Response(name, scores, data)
}

Json.parse("""{
  "name":"some_name",
  "scores": "[[10,15]]",
  "data": "{\"attr1\":\"value1\",\"attr2\":\"value3\"}"
}""").validate[Response]
// JsSuccess(Response(some_name,Vector(Vector(10, 15)),Map(attr1 -> value1, attr2 -> value3)),)