Search code examples
jsonscalaplayframeworkplay-json

Play Json Reads nested generic serialized Json


Consider the following JSON

{
    "a": "{\"b\": 12, \"c\": \"test\"}"
}

I would like to define a generic reads Reads[Outer[T]] for this kind of serialized Json

import play.api.libs.json.{Json, Reads, __}

final case class Outer[T](inner: T)
final case class SpecializedInner(b: Int, c: String)

object SpecializedInner {
   implicit val reads: Reads[SpecializedInner] = Json.reads[SpecializedInner]
}

object Outer {
   implicit def reads[T](implicit readsT: Reads[T]): Reads[Outer[T]] = ???
}

How can I achieve my goal? I tried to flatMap a string-reads for the field "outer.a" but got stuck since I am not able to produce a Reads[T] from the validated JSON

object Outer {
  implicit def reads[T](implicit readsT: Reads[T]): Reads[Outer[T]] =
    (__ \ "a").read[String].flatMap(x => Json.parse(x).validate[T])
}

Solution

  • You need just add map inside Outer.reads construction after validate[T] invocation.

    Please, see next code for example:

    object App {
    
      final case class Outer[T](inner: T)
    
      object Outer {
        implicit def reads[T](implicit innerReads: Reads[T]): Reads[Outer[T]] = { json: JsValue =>
          json.validate[String].flatMap(string => Json.parse(string).validate[T].map(Outer.apply[T]))
        }
      }
    
      final case class Root[T](a: Outer[T])
      object Root {
        implicit def reads[T](implicit innerReads: Reads[T]): Reads[Root[T]] = Json.reads
      }
    
      final case class SpecializedInner(b: Int, c: String)
    
      object SpecializedInner {
        implicit val reads: Reads[SpecializedInner] = Json.reads
      }
    
      def main(args: Array[String]): Unit = {
        val rawJson  = "{\"a\": \"{\\\"b\\\": 12, \\\"c\\\": \\\"test\\\"}\"}"
        println(Json.parse(rawJson).validate[Root[SpecializedInner]])
      }
    }
    

    Which produced next result in my case:

    JsSuccess(Root(Outer(SpecializedInner(12,test))),)
    

    Hope this helps!