I've been trying several things now to get this API to work, but somehow it just won't. The array of logs will not convert to List[Log]. I'm using reactivemongo and the play framework for Scala.
Via a web platform, the following json is arriving at the API:
{
name: 'name',
logs: [{timestamp: 1, activity: 'something'}]
}
Now I've got two models, one for each of the Objects:
import play.api.libs.json.Json
case class User(name: String, logs: List[Log])
object User { implicit val userFormatter = Json.format[User]
import play.api.libs.json.Json
case class Log(timestamp: String, activity: String)
object Log { implicit val logFormatter = Json.format[Log]
In the controller, I'm trying to read the JsObject as follows:
@Singleton
class UserController @Inject()(val reactiveMongoApi: ReactiveMongoApi)(implicit exec: ExecutionContext) extends Controller with MongoController with ReactiveMongoComponents {
val transformer: Reads[JsObject] =
Reads.jsPickBranch[JsString](__ \ "name") and
Reads.jsPickBranch[JsArray](__ \ "logs") reduce
//get the user collection from db
def persons: JSONCollection = db.collection[JSONCollection]("user")
var clazz: Class[_ <: JsObject] = _
//create a new user entry
def create(name: String,
logs: List[Log]) = Action.async {
val json = Json.obj(
"name" -> name,
"logs" -> logs)
persons.insert(json).map(lastError =>
Ok("Mongo LastError: %s".format(lastError)))
}
The route is as follows:
POST /user/add controllers.UserController.create(name: String, logs: List[models.Log])
It tells me to [error] /ARest-api/conf/routes:26: No QueryString binder found for type List[models.Log]. Try to implement an implicit QueryStringBindable for this type.
Which I have tried. I replaced the logFormatter line of code with the following:
implicit def LogBinder(implicit stringBinder: QueryStringBindable[String]) =
new QueryStringBindable[Log] {
override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Log]] = {
Some({
val timestamp = stringBinder.bind(key + ".timestamp", params)
val activity = stringBinder.bind(key + ".activity", params)
(timestamp, activity) match {
case (Some(Right(timestamp)), Some(Right(activity))) => Right(Log(timestamp, activity))
case _ => Left("Unable to bind Log")
}
})
}
override def unbind(key: String, log: Log): String =
stringBinder.unbind(
key + ".timestamp", log.timestamp) +
"&" + stringBinder.unbind(key + ".activity", log.activity)
}
Unfortunately this does not work. I've been struggling with this for several days now, and couldn't get it to work. Could someone please tell me if there perhaps is a better way to do this, or help me fix arrayreading?
Thanks in advance!