Pergunta

I have a List[play.api.libs.json.JsObject] returned from a MongoDB with the following document schema:

{
  "web_category_id" : "blah",
  "web_category_name" : "abcabc",
  "top_group_id" : "blah",
  "top_group_name" : "namehere",
  "sub_web_category" : [
    {
      "name" : "blah",
      "id" : "idblah"
    },
            {
      "name" : "another blah",
      "id" : "another idblah"
    } 

  ]

}

I am trying to use an implicit reader to read the data into a WebCategory case class:

case class WebCategory(topGroupName: String,
                       topGroupID: String,
                       webCategoryName : String,
                       webCategoryID : String,
                       subWebCats:Seq[SubWebCat])

case class SubWebCat(name:String, id:String)

My code so far (which is a million miles from compiling) is:

implicit val webCategoryReader = (
  (__/ "top_group_name").read[String] and
  (__/ "top_group_id").read[String] and
  (__/ "web_category_name").read[String] and
  (__/ "web_category_id").read[String] and
  (__/ "sub_web_category").read[List[Map[String, String]].map("name", "id"))
)(WebCategory)

I don't even know if it is possible to build a case class which includes another case class as one of its values.

Would really appreciate some help here, thanks!

EDIT:

Ok, so it turns out that I had defined a blank companion object on the SubWebCategory which (for reasons beyond me) interfered with the reader. Probably due to there not being an apply function to use in the over ridden companion object. Anyone care to confirm that?

Also, I was being dim and the WebCategory was actually defined as:

case class WebCategory(topGroupName: String,
                   topGroupID: String,
                   webCategoryName : String,
                   webCategoryID : String,
                   subWebCats:Option[Seq[SubWebCat]])

so the answer provided by Travis Brown above should have been (my bad, not his!) :

implicit val webCategoryReader: Reads[WebCategory] = (
  (__ \ "top_group_name").read[String] and
  (__ \ 'top_group_id).read[String] and
  (__ \ 'web_category_name).read[String] and
  (__ \ 'web_category_id).read[String] and
  (__ \ 'sub_web_category).read[Option[List[SubWebCat]]]
    )(WebCategory)

Thanks for your help, Travis

Foi útil?

Solução

You're actually very close—you just need a few small changes. First, you can use nested case classes, but you'll need Reads instances for each of them. I'll start with the inner one:

implicit val subWebCatReader: Reads[SubWebCat] = (
  (__ \ 'name).read[String] and (__ \ 'id).read[String]
)(SubWebCat)

Note that I'm using symbols instead of strings in the paths; this is entirely a matter of personal preference (it's always good to be consistent, though). Note also the direction of the slashes—they should be backward, not forward.

We can apply these changes to your code and make one additional edit (to the part that reads the subcategory field) and we're done:

implicit val webCategoryReader: Reads[WebCategory] = (
  (__ \ "top_group_name").read[String] and
  (__ \ 'top_group_id).read[String] and
  (__ \ 'web_category_name).read[String] and
  (__ \ 'web_category_id).read[String] and
  (__ \ 'sub_web_category).read[List[SubWebCat]]
)(WebCategory)

We know how to read a SubWebCat, so we also know how to read a List[SubWebCat]—no need to go through a map.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top