Search code examples
jsonscalaplayframeworkplayframework-2.2playframework-json

PlayFramework 2.2 - Scala - Read Mixed Arrays


I'm trying to use the reader/combinator things to parse an array that contains mixed data-types, but I'm not sure how to specify a reader for such a thing. I have some JSON like this:

{
  "stuff": [1, 2, true, null, "false", "hahaha", 5, "8"]
}

I want to parse all of this into a string-representation, but when I just try to use a reader of List[String] I get parse exceptions. I imagine something like this:

 implicit val mixedArrayReader = (
       (__ \ 'not).readNullable[List[JsValueWrapper]].map(opt => opt.map(list =>
             list.map { wrapper : JsValueWrapper =>
                val parsed : String = wrapper match {
                      case b : JsBoolean => if (b.value) "1" else "0"
                      case n : JsNumber  => n.value.toString
                      case s : JsString  => s.value
                      case JsNull        => "null"
                      case u             => u.toString  // unknown
                   }
                parsed
                }
          ))
 )

However, since there is no reader for JsValueWrapper, I'm not sure where to go from here. Any help is greatly appreciated.

Thanks!


Solution

  • Start by implementing a customized Reads[String], then use Reads.list to turn it into a reader of List[String], then use that reader as usual. Prefer to pass those particular readers explicitly versus defining implicits for them in order to not hide Play's default Reads[String].

    import play.api.libs.json.Reads
    
    val myReader: Reads[String] = Reads[String](value => JsSuccess(value match {
      case b : JsBoolean => if (b.value) "1" else "0"
      case n : JsNumber  => n.value.toString
      case s : JsString  => s.value
      case JsNull        => "null"
      case u             => u.toString  // unknown
    }))
    
    val listReads = Reads.list[String](myReader)
    

    Then, in a combined reader you can write something like:

    implicit val objReader =
      (__ \ 'not).readNullable(listReads)