2

I have a Play-scala v2.3 application. Looking from this guide about Json Combinators, I'm trying to do this:

object Application extends Controller {

  case class Foo(id: String, docs: List[Map[String, _]])

  implicit val fooReads = (
    (JsPath \ "id").read[String] and
    (JsPath \ "docs").read[List[Map[String, _]]]
  )(Foo.apply _)

  ...
}

But then I got compile error:

No Json deserializer found for type List[Map[String, _]]. Try to implement an implicit Reads or Format for this type.
       (JsPath \ "docs").read[List[Map[String, _]]]
                             ^

This is the example json that need to be read:

{
    "id": "001",
    "docs": [
        {
            "name": "Billy",
            "level": 2,
            "marked": false
        },
        {
            "name": "Fred",
            "level": 5,
            "marked": true
        }
    ]
}

I also have tried this:

  case class Foo(id: String, docs: Map[String, _])

  implicit val fooReads = (
    (JsPath \ "id").read[String] and
    (JsPath \ "docs").read[Map[String, _]]
  )(Foo.apply _)

Same error too.

It seems Play's JSON combinator doesn't work for Map type. Anyone know how to solve this?

5
  • It can work for the Map type, but not Map[String, _]. The _ is untyped, so it won't know what to do with it. Why are you trying to use Map[String, _] in the first place? Commented Mar 9, 2015 at 19:51
  • @m-z: I read from here, it said Json Macros are known to accept Option/Seq/List/Set & Map[String, _], could it be a hint that it should work (at past?). The reason to use Map because I have a service method that accept List[Map[String, _]] parameter, although I can use a list of case class for the docs, but then I have to convert it to List[Map[String, _]]. Commented Mar 9, 2015 at 19:53
  • I don't think it's meant to literally say Map[String, _], but Map[String, Int], Map[String, String], etc.. for types that make sense. Commented Mar 9, 2015 at 19:56
  • So it's impossible? The docs json doesn't have uniform type value, that's why I have to use Map[String, _]. Commented Mar 9, 2015 at 19:58
  • Like that it's not possible without creating your own custom Reads/Writes for it. If they don't have a uniform type, then what do they have? It's still possible to make it work in some way, just not with the macro. Commented Mar 9, 2015 at 20:00

1 Answer 1

4

You'd be a lot better off avoiding Map[String, Any], and Scala and Play make this easy. A much better solution is to define a custom case class for the thing you're trying to represent as a map:

import play.api.libs.json._

case class Doc(name: String, level: Int, marked: Boolean)
case class Foo(id: String, docs: List[Doc])

implicit val docFormat = Json.format[Doc]
implicit val fooFormat = Json.format[Foo]

val json = Json.parse(...)

And then:

scala> json.as[Foo]
res0: Foo = Foo(001,List(Doc(Billy,2,false), Doc(Fred,5,true)))

Or if you want more control:

import play.api.libs.functional.syntax._
import play.api.libs.json._

implicit val fooReads: Reads[Foo] = (
  (__ \ 'id).read[String] and
  (__ \ 'docs).read(
    Reads.list((
      (__ \ 'name).read[String] and
      (__ \ 'level).read[Int] and
      (__ \ 'marked).read[Boolean]
    )(Doc.apply _))
  )
)(Foo.apply _)

If you really need a Map[String, Any], you can always write a conversion method on Doc.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.