LXSoft
LXSoft

Reputation: 596

How to store sequence of JsValue into a ArrayBuffer

I have the following json object what contain a list of json objects. Each of these objects contain informations about a station object.

"stations": [
            {
                "cryptName": "Tv_Gara Nord 1",
                "easyName": "Gara de Nord",
                "lat": 45.750287,
                "lng": 21.207498
            },
            {
                "cryptName": "Tv_R Carol 1",
                "easyName": "Regele Carol I",
                "lat": 45.745068,
                "lng": 21.211034
            },
            {
                "cryptName": "Tv_Mocioni 1",
                "easyName": "Piata Mocioni (Sinaia)",
                "lat": 45.746343,
                "lng": 21.21551
            }]

I've tried this and I am unable to convert this to a ArrayBuffer with format:

ArrayBuffer(List("cryptName", "easyName", "lat", "lng"), ...)

[Line 73 .. 77] The problem is with (js \\\ "cryptName")(0) ... (js \\\ "lng")(0).

How to store correctly all of these stations not a single one into my ArrayBuffer?

    package backend

    import scala.concurrent.duration._
    import scala.collection.immutable.Seq
    import scala.concurrent.forkjoin.ThreadLocalRandom
    import akka.actor.{ ActorRef, Props }
    import play.api.libs.json.Json
    import play.extras.geojson.{ LineString, LatLng, FeatureCollection }
    import play.api.Logger
    import actors.GeoJsonBot
    import java.net.URL
    import akka.actor.Actor
    import scala.io.Source
    import akka.cluster.Cluster
    import play.api.libs.json.JsValue
    import org.apache.commons.lang3.Validate

    object BotManager
    {
        def props(regionManagerClient: ActorRef, data: Seq[URL]): Props = Props(new BotManager(regionManagerClient, data))
        private case object Tick
    }

    /**
     * Loads and starts GeoJSON bots
     */
     class BotManager(regionManagerClient: ActorRef, data: Seq[URL]) extends Actor
    {
        import BotManager._

        var total = 0
    val max = Settings(context.system).TotalNumberOfBots

    import context.dispatcher
    val tickTask = context.system.scheduler.schedule(1.seconds, 3.seconds, self, Tick)
    val port = Cluster(context.system).selfAddress.port.get

    override def postStop(): Unit = tickTask.cancel()

    def receive =
    {
        case Tick if total >= max => tickTask.cancel()
        case Tick =>
            val totalBefore = total
            val originalTrail = total == 0
            println("NODE: " + data)
            data.zipWithIndex.foreach
            {
                case (url, id) =>
                    val json = Json.parse(Source.fromURL(url).mkString)
                    println("URL: " + url + "ID: " + id)
                    Json.fromJson[FeatureCollection[LatLng]](json).fold(
                    {
                        invalid =>
                        Logger.error("Error loading geojson bot: " + invalid)
                    }, valid => valid.features.zipWithIndex.map
                    {
                        feature => feature._1.geometry match
                        {
                            case route: LineString[LatLng] if total < max => total += 1
                            val userId = "bot-" + total + "-" + port + "-" + id + "-" + feature._1.id.getOrElse(feature._2) + "-" + feature._1.properties.flatMap(js => (js \ "name").asOpt[String]).getOrElse("") + feature._1.properties.flatMap(js => (js \ "segment").asOpt[String]).getOrElse("")
                            val stations = feature._1.properties.flatMap(js => (js \ "stations").asOpt[JsValue])

                            import play.api.libs.json.JsObject
                            import play.api.libs.json.JsArray
                            var stationss = feature._1.properties.flatMap(js => (js \ "stations").asOpt[JsValue])
                            val crypt = stationss.flatMap(js => (js \\ "cryptName")(0).asOpt[JsValue])
                            val ename = stationss.flatMap(js => (js \\ "easyName")(0).asOpt[JsValue])
                            val lat   = stationss.flatMap(js => (js \\ "lat")(0).asOpt[JsValue])
                            val lng   = stationss.flatMap(js => (js \\ "lng")(0).asOpt[JsValue])

                            case class Station(cryptName: String, easyName: String, lat: Float, lng: Float)
                            implicit val readsStation = Json.reads[Station]
                            case class Stations(stations: List[Station])
                            implicit val readsStations = Json.reads[Stations]

                            val stations = Json.parse(text).as[Stations].stations.map(s => List(s.cryptName, s.easyName, s.lat, s.lng)).to[ArrayBuffer]

                            println("List: [" + crypt.get + ", " + ename.get + ", " + lat.get + ", " + lng.get + "]")

                            val offset =
                                if (originalTrail) (0.0, 0.0)
                                else (ThreadLocalRandom.current.nextDouble() * 15.0,
                            ThreadLocalRandom.current.nextDouble() * -30.0)
                            context.actorOf(GeoJsonBot.props(route, stations, offset, userId, regionManagerClient))
                            case other =>
                        }
                    })
            }

            println("Started " + (total - totalBefore) + " bots, total " + total)
    }
}

Upvotes: 0

Views: 833

Answers (2)

bjfletcher
bjfletcher

Reputation: 11518

I'd do it like this:

case class Station(cryptName: String, easyName: String, lat: Float, lng: Float)
implicit val readsStation = Json.reads[Station]
case class Stations(stations: List[Station])
implicit val readsStations = Json.reads[Stations]

(assuming JSON is { stations: [ ... ] })

Then use the following:

val stations = Json.parse(text).as[Stations].stations
    .map(s => List(s.cryptName, s.easyName, s.lat, s.lng)).to[ArrayBuffer])

which'll produce the following (given your sample):

ArrayBuffer(List(Tv_Gara Nord 1, Gara de Nord, 45.750286, 21.207499), List(Tv_R Carol 1, Regele Carol I, 45.745068, 21.211035), List(Tv_Mocioni 1, Piata Mocioni (Sinaia), 45.74634, 21.21551))

Upvotes: 2

Johny T Koshy
Johny T Koshy

Reputation: 3922

Use Reads\Writes macro.

Create case classes, something like,

case class Station(cryptName: String, easyName: String, lat: String, long: String)
case class Stations(stations: Seq[Station])

Create reads,

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val stationReads = Json.reads[Station]
implicit val stationsReads = Json.reads[Stations]

And do conversion,

val json: JsValue = Json.parse(jsonString)
Json.fromJson[Stations](json) match {
  case JsSuccess(t, _) => ???
  case any: JsError    => ???
}

Go through read/write combinators and read/write macros

Upvotes: 1

Related Questions