1

I'm using Scala and Swagger and i need help figuring out how to loop through the values in the json and use those values for checking and others.

The json string that is returned after HTTP get request looks like this:

{
"count": 3,
"items": [
  {
    "Id": "fd0a9e5a",
    "DbName": "Xterior Prod",
    "Name": "XP"
  },
  {
   "Id": "4158a1a6",
   "DbName": "Invidibi Pappear",
   "Name": "ISP"
  },
  {
   "Id": "7e0c57046d3f",
   "DbName": "Multi Test",
   "Name": "MMP"
  }]
}

My UI allows the user to input an ID. What i have to do is to loop through the Json value returned from the API and find the one that matches the ID entered. Once i find a match, i have to check if the database has "Test" keyword in it. If it does, i will need to show the DbName and the shortname.

I have found some guide here (e.g. Foreach with JSON Arrays in Play2 and Scala) but it did not work for me. When i run my code, i get this error:

play.api.libs.json.JsResultException: JsResultException(errors:List(((0)/Id,List(ValidationError(List(error.path.missing),WrappedArray()))), ((0)/DbName,List(ValidationError(List(error.path.missing),WrappedArray()))), ((1)/Id,List(ValidationError(List(error.path.missing),WrappedArray()))), ((1)/DbName,List(ValidationError(List(error.path.missing),WrappedArray()))), ((2)/Id,List(ValidationError(List(error.path.missing),WrappedArray()))), ((2)/DbName,List(ValidationError(List(error.path.missing),WrappedArray()))), 

Here is my code:

case class DBInfo(Id: String, DbName: String, Name: String)
contentType = "application/json"
    //get json from http

    val httpClient = HttpClients.createDefault()
    val httpResponse = httpClient.execute(new HttpGet("http://www.customers.com/dbInfo"))
    val entity = httpResponse.getEntity

    val content = fromInputStream(httpResponse.getEntity.getContent()).getLines().mkString

    implicit val dbReader = Json.reads[DBInfo]
    val dbList = (Json.parse(content) \ "items").as[List[DBInfo]]
    dbList.foreach { dbI =>
      if (dbI.Id == id)
        if (dbI.DbName.contains("Test"))
          println(dbI.DbName + " - " + dbI.Name)
          else BadRequest("Not allowed")
      else
        BadRequest("ID not found")
    }

id is the variable that holds the inputed ID by the user. Can someone tell me why the error and how to fix it? Thanks.

note: Please using import org.json4s.JsonAST or import play.api.libs.json

oneDerer
  • 287
  • 3
  • 10

2 Answers2

1

already got the answer. so this is how i did it:

case class databaseInfo(Id: String, DbName: String, Name: String)
class dbInfo{
def CheckDb(id: String): Option[String] = {
    val httpClient = HttpClients.createDefault()
    val httpResponse = httpClient.execute(new HttpGet("http://example.com"))
    val content = fromInputStream(httpResponse.getEntity.getContent()).getLines().mkString

    val envItems = (parse(content) \\ "items").children
    for (items <- envItems) {
      val dbItems = items.extract[databaseInfo]
      if (dbItems.EnvId == Some(id)) {
        if (equalsIgnoreCase(dbItems.DbName.mkString, "Test")) //do something
        else //do something
      }
    }
    None
  }
}
oneDerer
  • 287
  • 3
  • 10
0

Here is an approach using circe. You can navigate the JSON with a Cursor, and decode to a list of Environment using the Decoder[A] typeclass. Note that you work with Either[Failure, A] values.

import io.circe._

case class Environment(id: String, dbName: String, name: String)

implicit val environmentDecoder: Decoder[Environment] = Decoder.instance[Environment] {
  json =>
    for {
      id <- json.downField("Id").as[String]
      dbName <- json.downField("DbName").as[String]
      name <- json.downField("Name").as[String]
    } yield {
      Environment(id, dbName, name)
    }
}

// alternatively:
// implicit val environmentDecoder: Decoder[Environment] =
//   Decoder.forProduct3[String, String, String, Environment]("Id", "DbName", "Name")(Environment.apply)


val text =
  """{
    |  "count": 3,
    |  "items": [{
    |    "Id": "fd0a9e5a",
    |    "DbName": "Xterior Prod",
    |    "Name": "XP"
    |  }, {
    |    "Id": "4158a1a6",
    |    "DbName": "Invidibi Pappear",
    |    "Name": "ISP"
    |  }, {
    |    "Id": "7e0c57046d3f",
    |    "DbName": "Multi Match Test",
    |    "Name": "MMP"
    |  }]
    |}
  """.stripMargin

val json = parser.parse(text).fold(_ => ???, json => json)


val res: Either[DecodingFailure, List[Environment]] = json.hcursor.downField("items").as[List[Environment]]


println(res)
// Right(List(Environment(fd0a9e5a,Xterior Prod,XP), Environment(4158a1a6,Invidibi Pappear,ISP), Environment(7e0c57046d3f,Multi Match Test,MMP)))


// or simply
// val res2 = parser.parse(text).right
//   .flatMap(_.hcursor.downField("items").as[List[Environment]])

You can also use http4s' http4s-blaze-client and http4s-circe to do HTTP requests:

import org.http4s._
import org.http4s.circe._
import scalaz.concurrent._

val client = org.http4s.client.blaze.defaultClient

val fetchEnvironments: Task[List[Environment]] =
  client.fetchAs[Json](Request(Method.GET, Uri.uri("http://example.com")))
    .flatMap { json =>
      json.hcursor.downField("items").as[List[Environment]].fold(
        failure => Task.fail(failure),
        xs => Task.now(xs)
      )
    }

val xs = fetchEnvironments.unsafePerformSync
Stefan Ollinger
  • 1,577
  • 9
  • 16
  • hi, is there a way to implement this using json4s or play? these are already the way json is implemented in the current project and i would prefer using these same libraries for consistency. – oneDerer Jan 23 '17 at 03:03