简体   繁体   中英

Iterating over an array in JSON with Play 2.1.1

I am using play 2.1.1 and I am having issues iterating through an array. I had read somewhere that you can create a reads for a List[Object] but everytime I try to do this I get an error "No unapply function found" for the line that says

implicit val userListReads: Reads[List[FBUser]] = Json.reads[List[FBUser]]". The issue is " = Json.reads[List[FBUser]]

I am at a loss of what else to try. Any assistance would be greatly appreciated.

def linkUsers() = Action { implicit request =>
  val json = Json.parse("{\"data\": [{\"name\": \"Me Lazyan\",\"id\": \"1182\"},{\"name\": \"Chales Dselle\",\"id\": \"10115\"},{\"name\": \"Be My\",\"id\": \"10275\"},{\"name\": \"De Rwani\", \"id\": \"11189\"},{\"name\": \"Phoe Johon\", \"id\": \"11372\"}]}")

  val peoples = json.validate[List[FBUser]].get
  peoples.foreach(println)

  Ok(json).withHeaders(CONTENT_TYPE -> "text/json")
}

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

object FBUser {
    /** Uses a Scala Macro to define the Reads function */
    implicit val userReads: Reads[FBUser] = Json.reads[FBUser]
    implicit val userListReads: Reads[List[FBUser]] = Json.reads[List[FBUser]]
}

Purpose

Even if the the @martin's answer is giving the straight solution regarding the mismatch between the json message and the expected result, I'd like to give you some advices for a clean way to reach your goal.

That's because, AFAICK, you're redefining way too much things and you could meet cases where errors aren't reported... but just hidden by a different error like NoSuchElementException.

Lastly, I would like to give a solution where your message is kept unchanged (like the second @Martin 's solution was).

Proposition

The idea is to keep things packaged with their responsibilities also the code is split in three different files:

FBUser.scala which simply declares the model structure

package models

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

formats.scala which is a package that will gather all formats definition, specially the json ones:

package models

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.json.util._
import play.api.libs.json.Json._
import play.api.libs.functional.syntax._

package formats {

  object jsons {
    implicit val fbUserFormat:Format[FBUser] = Json.format[FBUser]
  }

}

Note that the format for List[FBUser] is not provided because the json api will resolve to the implicit and generic Reads and OWrites .

And lastly, the Application.scala that will contain code only relevant to some use cases, like the json object containing a data field, the list of FBUser .

package controllers

import play.api._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.json.Json._

import models._

object Application extends Controller {

  import models.formats.jsons._
  val readUserFromInput = (__ \ 'data).read[List[FBUser]]

  def index = Action {
    val jsonString = "{\"data\": [{\"name\": \"Me Lazyan\",\"id\": \"1182\"},{\"name\": \"Chales Dselle\",\"id\": \"10115\"},{\"name\": \"Be My\",\"id\": \"10275\"},{\"name\": \"De Rwani\", \"id\": \"11189\"},{\"name\": \"Phoe Johon\", \"id\": \"11372\"}]}"

    val json = Json.parse(jsonString)
    val users = json.validate(readUserFromInput)

    users.map(
      list => Ok(Json.toJson(list)) // or Ok(json) to match exactly you example
    ).recoverTotal{
      err => BadRequest(JsError.toFlatJson(err))
    }
  }

}

In this controller, we can see that it defines the specific Reads for the initial case, thus the access to the data field and the reads to FBUser instance are safe thanks to the use of validate , map and recoverTotal .

Some words

A last note about safety, Json.parse is not safe so in order to be even more safe you should consider some options depending on your workflows, some of them are: * using the json body parser that'll let you handle bad json format explicitly * using a dedicated case class defined in the controller for this particular case (and then define its Reads/... using inception like for FBUser )

Your json value is an object with field data containing an array. You are trying to parse the single object as an array. You would either have to change json to.

val json = Json.parse("[{\"name\": \"Me Lazyan\",\"id\": \"1182\"},{\"name\": \"Chales Dselle\",\"id\": \"10115\"},{\"name\": \"Be My\",\"id\": \"10275\"},{\"name\": \"De Rwani\", \"id\": \"11189\"},{\"name\": \"Phoe Johon\", \"id\": \"11372\"}]")

or change your code to

val people = (json \ "data").validate[List[FBUser]].get

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM