Home > OS >  Mapping a string to case classes using Scala play
Mapping a string to case classes using Scala play

Time:10-12

Using the Scala play library I'm attempting to parse the string :

  var str = "{\"payload\": \"[{\\\"test\\\":\\\"123\\\",\\\"tester\\\":\\\"456\\\"},"  
    "{\\\"test1\\\":\\\"1234\\\",\\\"tester2\\\":\\\"4567\\\"}]\"}";

into a list of Payload classes using code below :

import play.api.libs.json._

object TestParse extends App {
  
  case class Payload(test : String , tester : String)
  object Payload {
    implicit val jsonFormat: Format[Payload] = Json.format[Payload]
  }

  var str = "{\"payload\": \"[{\\\"test\\\":\\\"123\\\",\\\"tester\\\":\\\"456\\\"},"  
    "{\\\"test1\\\":\\\"1234\\\",\\\"tester2\\\":\\\"4567\\\"}]\"}";

  println((Json.parse(str) \ "payload").as[List[Payload]])

}

build.sbt :

name := "akka-streams"

version := "0.1"

scalaVersion := "2.12.8"

lazy val akkaVersion = "2.5.19"
lazy val scalaTestVersion = "3.0.5"

libraryDependencies   = Seq(
  "com.typesafe.akka" %% "akka-stream" % akkaVersion,
  "com.typesafe.akka" %% "akka-stream-testkit" % akkaVersion,
  "com.typesafe.akka" %% "akka-testkit" % akkaVersion,
  "org.scalatest" %% "scalatest" % scalaTestVersion
)

// https://mvnrepository.com/artifact/com.typesafe.play/play-json
libraryDependencies  = "com.typesafe.play" %% "play-json" % "2.10.0-RC6"

It fails with exception :

Exception in thread "main" play.api.libs.json.JsResultException: JsResultException(errors:List((,List(JsonValidationError(List("" is not an object),WrappedArray())))))

Is the case class structure incorrect ?

I've updated the code to :

import play.api.libs.json._

object TestParse extends App {

  import TestParse.Payload.jsonFormat
  object Payload {
    implicit val jsonFormat: Format[RootInterface] = Json.format[RootInterface]
  }
  case class Payload (
                       test: Option[String],
                       tester: Option[String]
                     )

  case class RootInterface (
                             payload: List[Payload]
                           )

  val str = """{"payload": [{"test":"123","tester":"456"},{"test1":"1234","tester2":"4567"}]}"""

  println(Json.parse(str).as[RootInterface])

}

which returns error :

No instance of play.api.libs.json.Format is available for scala.collection.immutable.List[TestParse.Payload] in the implicit scope (Hint: if declared in the same file, make sure it's declared before) implicit val jsonFormat: Format[RootInterface] = Json.format[RootInterface]

CodePudding user response:

This performs the task but there are cleaner solutions :

import akka.actor.ActorSystem
import akka.stream.scaladsl.{Flow, Sink, Source}
import org.scalatest.Assertions._
import spray.json.{JsObject, JsonParser}

import scala.concurrent.Await
import scala.concurrent.duration.DurationInt

object TestStream extends App {
  implicit val actorSystem = ActorSystem()
  val mapperFlow = Flow[JsObject].map(x => {
    x.fields.get("payload").get.toString().replace("{", "")
      .replace("}", "")
      .replace("[", "")
      .replace("]", "")
      .replace("\"", "")
      .replace("\\", "")
      .split(":").map(m => m.split(","))
      .toList
      .flatten
      .grouped(4)
      .map(m => Test(m(1), m(3).toDouble))
      .toList
  })

  val str = """{"payload": [{"test":"123","tester":"456"},{"test":"1234","tester":"4567"}]}"""
  case class Test(test: String, tester: Double)

val graph = Source.repeat(JsonParser(str).asJsObject())
  .take(3)
  .via(mapperFlow)
  .mapConcat(identity)
  .runWith(Sink.seq)

  val result = Await.result(graph, 3.seconds)

  println(result)
  assert(result.length == 6)
  assert(result(0).test == "123")
  assert(result(0).tester == 456 )
  assert(result(1).test == "1234")
  assert(result(1).tester == 4567 )
  assert(result(2).test == "123")
  assert(result(2).tester == 456 )
  assert(result(3).test == "1234")
  assert(result(3).tester == 4567 )

}

Alternative, ioiomatic Scala answers are welcome.

  • Related