Simplest way to read an array into a list of custo

2019-08-12 16:57发布

问题:

I'm working with Play JSON API (latest version; Play 2.4), reading incoming JSON into objects.

When writing JSON, there's absolutely no problem in using a list of custom objects, as long as I have implicit val writes = Json.writes[CustomType].

But apparently the inverse is not true, as the following does not work even though Reads is generated for both top-level type and list item type (using Json.reads[Incoming] and Json.reads[Item]). Is custom Reads implementation mandatory? Or am I missing something obvious? What is the simplest way to make this work?

Simplified example:

JSON:

{
  "test": "...",
  "items": [
     { "id": 44, "time": "2015-11-20T11:04:03.544" },
     { "id": 45, "time": "2015-11-20T11:10:10.101" }
  ]
}

Models/DTOs matching the incoming data:

import play.api.libs.json.Json

case class Incoming(test: String, items: List[Item])

object Incoming {
  implicit val reads = Json.reads[Incoming]
}


case class Item(id: Long, time: String)

object Item {
  implicit val reads = Json.reads[Item]
}

Controller:

def test() = Action(parse.json) { request =>
  request.body.validate[Incoming].map(incoming => {
     // ... handle valid incoming data ...
  }).getOrElse(BadRequest)
}

Compiler has this to say:

No implicit format for List[models.Item] available.
[error]   implicit val reads = Json.reads[Incoming]
                       ^
No Json deserializer found for type models.Incoming. 
Try to implement an implicit Reads or Format for this type.
[error]     request.body.validate[Incoming].map(incoming => {

回答1:

Try defining the case class and object for Item before those for Incoming. See this answer for more info: https://stackoverflow.com/a/15705581



回答2:

The issue may be in your imports in Controller. Just importing "Incoming" will import the case class. In order to import the implicit val try "Incoming._". That will import all members of the Object Incoming.