Ok this is really bugging me.I have implemented a REST api using spray and scala,akka. I have successfully implemented spray get, post routes to query mysql database and give the json output.I am able to send the parameters within the post body but however I am not able to send the same as JSON. Its looks very simple but its just not working.I am getting error - empty reply from server. Please Please help.
Here is my sample curl command:
curl -H "Content-Type: application/json" -X POST -d '{ "name": "Jane", "favNumber" : 42 }' http://localhost:8080/jsonTesting
I am trying this small sample code to see if it is possible to unmarshall json parameters and get the required output. If it works then I am implementing the same in my actual code.
import spray.json.DefaultJsonProtocol
import spray.json._
import spray.httpx.SprayJsonSupport
object MyJsonProtocol extends DefaultJsonProtocol with SprayJsonSupport {
implicit val Format = jsonFormat2(Person)
case class Person(name:String, favNumber:Int)
//Spray main class
import akka.actor.ActorSystem
import spray.routing.SimpleRoutingApp
import spray.json.DefaultJsonProtocol
import spray.json._
import spray.http.MediaTypes._
import MyJsonProtocol._
import spray.routing._
object SprayTest extends App with SimpleRoutingApp {
implicit val system = ActorSystem()
def testRoute(route: Route): Route = {
post {
respondWithMediaType(`application/json`){
route
}
}
}
startServer(interface = "localhost", port = 8080) {
testRoute {
path("departed") {
parameter('count.as[Boolean]) {count =>
complete(if (count == true) "even ball" else "odd ball")
}
}
} ~
path("jsonTesting") {
post{
entity(as[Person]) { person =>
complete(s"Person: ${person.name} - favoritenumber:${person.favNumber}")
}
}
}
}
}
Here is the error:
Uncaught error from thread [default-akka.actor.default-dispatcher-2] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[default]
java.lang.NoSuchMethodError: spray.json.JsonParser$.apply(Ljava/lang/String;)Lspray/json/JsValue;
at spray.httpx.SprayJsonSupport$$anonfun$sprayJsonUnmarshaller$1.applyOrElse(SprayJsonSupport.scala:36)
at spray.httpx.SprayJsonSupport$$anonfun$sprayJsonUnmarshaller$1.applyOrElse(SprayJsonSupport.scala:34)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
at spray.httpx.unmarshalling.Unmarshaller$$anon$1$$anonfun$unmarshal$1.apply(Unmarshaller.scala:29)
at spray.httpx.unmarshalling.SimpleUnmarshaller.protect(SimpleUnmarshaller.scala:40)
at spray.httpx.unmarshalling.Unmarshaller$$anon$1.unmarshal(Unmarshaller.scala:29)
at spray.httpx.unmarshalling.SimpleUnmarshaller.apply(SimpleUnmarshaller.scala:29)
at spray.httpx.unmarshalling.SimpleUnmarshaller.apply(SimpleUnmarshaller.scala:23)
at spray.httpx.unmarshalling.UnmarshallerLifting$$anon$3.apply(UnmarshallerLifting.scala:35)
Sbt dependecies:
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.3.6",
"com.typesafe.akka" %% "akka-http-experimental" % "0.7",
"io.spray" %% "spray-json" % "1.3.2",
"io.spray" %% "spray-routing" % sprayVersion,
"io.spray" %% "spray-client" % sprayVersion,
"io.spray" %% "spray-testkit" % sprayVersion % "test",
"mysql" % "mysql-connector-java" % "5.1.25",
"com.typesafe.slick" %% "slick" % "3.0.0",
"com.typesafe.scala-logging" %% "scala-logging-slf4j" % "2.1.2"
)
Related
The application I am developing has to decode json from a data source which for a given field may return either a List[String] or a List[Double]. I want to decode this json into a case class so i can process the data.
[{
"id": 123,
"latlng": ["-12.777", "18.776"]
}, {
"id": 123,
"latlng": [-12.777, 18.776]
}]
Im using circe 0.11.1
currently my case class looks like
case class Report(id:Int, latlng:Either[List[String],List[Double]])
and my decoding code
decode[List[Report]](testData)
i receive the error
DecodingFailure at [0].latlng: [A, B]Either[A, B]
It sounds like you already have a solution that works for you. I found a solution to your original problem though. It is not very elegant, but it works. There might be a more elegant solution that involves the Validated monad in Cats library, but I am not familiar enough with the Cats library to write it in that way.
import io.circe._
import io.circe.parser._
object Main {
def main(args: Array[String]) = {
val testData =
"""
|[{
| "id": 123,
| "latling": ["-12.777", "18.776"]
|}, {
| "id": 123,
| "latling": [-12.777, 18.776]
|}]
""".stripMargin
println(decode[List[Report]](testData))
}
case class Report(id: Int, latling: Either[List[String],List[Double]])
object Report {
implicit val reportDecoder: Decoder[Report] = new Decoder[Report] {
override def apply(c: HCursor): Decoder.Result[Report] = {
val stringAttempt = for {
id <- c.downField("id").as[Int]
latlingString <- c.downField("latling").as[List[String]]
} yield Report(id, Left(latlingString))
val doubleAttempt = for {
id <- c.downField("id").as[Int]
latlingDouble <- c.downField("latling").as[List[Double]]
} yield Report(id, Right(latlingDouble))
stringAttempt match {
case Right(stringValue) => Right(stringValue)
case Left(stringFailure) => doubleAttempt
}
}
}
}
}
You can run by starting up sbt with
sbt
and running
runMain Main
Here is my build.sbt file:
name := "stackoverflow20190821"
version := "0.1"
scalaVersion := "2.12.0"
val circeVersion = "0.11.1"
libraryDependencies ++= Seq(
"io.circe" %% "circe-core",
"io.circe" %% "circe-generic",
"io.circe" %% "circe-parser"
).map(_ % circeVersion)
I'm currently trying to create a table using slick and I'm baffled as to what import I'm missing as the examples I've seen don't seem to have a relevant looking import in them.
Currently the column, question mark and the O's are all unresolved.
Could someone let me know what I'm doing wrong please?
Here is my table class:
package com.grimey.tabledefinitions
import slick.driver.MySQLDriver.api._
import com.grimey.staticpage.StaticPage
import slick.lifted.Tag
import slick.model.Table
class StaticPageDef(tag: Tag) extends Table[StaticPage](tag, "static_page") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def pageType = column[String]("page_type")
def contentHtml = column[String]("content_html")
def * = (id.?, pageType, contentHtml) <>(StaticPage, StaticPage.unapply _)
}
And here is my build.sbt:
name := """grimey-cms"""
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayScala)
scalaVersion := "2.11.8"
libraryDependencies ++= Seq(
"mysql" % "mysql-connector-java" % "5.1.38",
"com.typesafe.play" %% "play-slick" % "2.0.0",
"com.typesafe.play" %% "play-slick-evolutions" % "2.0.0"
)
resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"
fork in run := true
And finally, here is the case class I'm using for the table:
package com.grimey.staticpage
import java.time.LocalDateTime
case class StaticPage(id: Long, htmlContent: String, pageType: String,
created: LocalDateTime, updated: LocalDateTime)
I bet it's something really silly :)
The O object is from the table and it varies driver to driver. Some drivers may not support certain column options supported by others. Therefore you will need to import the column options that are specific to your database - MySQL in this case:
import slick.driver.MySQLDriver.api._
You may check this full tutorial on how to use Play + Slick + MySQL: http://pedrorijo.com/blog/play-slick/
Or you may just navigate through the code: https://github.com/pedrorijo91/play-slick3-steps
I am upgrading Play Framework server from 2.2.2 to 2.4.4, as my application is working fine with old version, but while upgrading it is giving various errors like:
Cannot write an instance of play.api.libs.json.JsLookupResult to HTTP response. Try to define a Writable[play.api.libs.json.JsLookupResult]
Ok<gts<0>>
^
build.sbt:
name := """TestApp"""
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayJava)
//scalaVersion := "2.11.6"
//scalaVersion := "2.10.2"
libraryDependencies ++= Seq(
javaJdbc,
cache,
javaWs,
jdbc,
cache,
"com.typesafe.play" %% "anorm" % "2.4.0",
"com.google.inject" % "guice" % "4.0",
"javax.inject" % "javax.inject" % "1",
"org.reactivemongo" %% "reactivemongo" % "0.10.0",
"org.reactivemongo" %% "play2-reactivemongo" % "0.10.2",
"org.mockito" % "mockito-core" % "1.9.5" % "test",
"org.webjars" % "requirejs" % "2.1.1",
"org.postgresql" % "postgresql" % "9.4-1200-jdbc41",
"postgresql" % "postgresql" % "9.1-901.jdbc4",
"com.typesafe.play" % "play-iteratees_2.10" % "2.2.3",
"com.typesafe.slick" % "slick_2.10" % "2.1.0",
"com.typesafe.play" % "play-jdbc_2.10" % "2.4.4",
"org.apache.flume" % "flume-ng-core" % "1.5.2",
"org.apache.flume" % "flume-ng-sdk" % "1.5.2",
"org.scala-lang" % "scala-compiler" % "2.11.6"
)
app.scala:
package controllers
import play.modules.reactivemongo.MongoController
import play.modules.reactivemongo.json.collection.JSONCollection
import scala.concurrent.Future
import reactivemongo.api.Cursor
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import javax.inject.Singleton
import play.api.mvc._
import play.api.libs.json._
#Singleton
class Application extends Controller with MongoController {
def collection: JSONCollection = db.collection[JSONCollection]("Test")
import models._
import models.JsonFormats._
import reactivemongo.bson._
def index = Action.async {
val cursor: Cursor[JsValue] = collection.
find(Json.obj()).
cursor[JsValue]
val futureUsersList: Future[List[JsValue]] = cursor.collect[List]()
val futurePersonsJsonArray: Future[JsArray] = futureUsersList.map { dao =>
Json.arr(dao)
}
futurePersonsJsonArray.map {
dao =>
Ok(dao(0))
}
}
}
Please let me know where I am doing wrong whether it is in my controller class or build.sbt ? Thanks in advance.
Indexing a JsArray does not return the JsValue at that index but a JsLookupResult, which is either JsDefined or JsUndefined (if, e.g, the index is out-of-bounds.) I think this changed in Play 2.3.
While you can write a JsValue directly to an HTTP response via the default JSON writeable provided by Play, there is no such writeable for a JsLookupResult. You can fix it by pattern matching on the JsLookupResult to extract the value, and handle its absence:
futurePersonsJsonArray.map { dao =>
dao(0).match {
case JsDefined(js) => Ok(js)
case _ => NotFound
}
}
or use toOption to get an Option[JsValue]:
futurePersonsJsonArray.map { dao =>
dao(0).toOption.map(js => Ok(js)).getOrElse(NotFound)
}
However, a better way to fix the code would be to use Json.toJson() directly on the redeemed result of futureUserList, rather than putting it in a JSON array, and then immediately taking it out again:
def index = Action.async {
val cursor: Cursor[JsValue] = collection.
find(Json.obj()).
cursor[JsValue]
val futureUsersList: Future[List[JsValue]] = cursor.collect[List]()
futureUsersList.map { list =>
Ok(Json.toJson(list))
}
}
The methods in ADSRegistrationMap are used to save and retrieve the document from MongoDB. ObjectId is created during initialization. I have to do the same to load Registration from Json that is part of POST body, so I thought I could just add ADSRegistrationProtocol object to do that. It fails with compilation error. Any idea on how to fix it or do this better?
package model
import spray.json._
import DefaultJsonProtocol._
import com.mongodb.casbah.Imports._
import org.bson.types.ObjectId
import com.mongodb.DBObject
import com.mongodb.casbah.commons.{MongoDBList, MongoDBObject}
case class Registration(
system: String,
identity: String,
id: ObjectId = new ObjectId())
object RegistrationProtocol extends DefaultJsonProtocol {
implicit val registrationFormat = jsonFormat2(Registration)
}
object RegistrationMap {
def toBson(registration: Registration): DBObject = {
MongoDBObject(
"system" -> registration.system,
"identity" -> registration.identity,
"_id" -> registration.id
)
}
def fromBson(o: DBObject): Registration = {
Registration(
system = o.as[String]("system"),
identity = o.as[String]("identity"),
id = o.as[ObjectId]("_id")
)
}
}
Compilation Error:
[error] /model/Registration.scala:20: type mismatch;
[error] found : model.Registration.type
[error] required: (?, ?) => ?
[error] Note: implicit value registrationFormat is not applicable here because it comes after the application point and it lacks an explicit result type
[error] implicit val registrationFormat = jsonFormat2(Registration)
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
Updated ObjectId to String and jsonFormat2 to jsonFormat3 to fix the compilation error.
case class Registration(
system: String,
identity: String,
id: String = (new ObjectId()).toString())
object RegistrationProtocol extends DefaultJsonProtocol {
implicit val registrationFormat = jsonFormat3(Registration)
}
Getting runtime error now when converting body of POST request to the Registration object. Any idea?
val route: Route = {
pathPrefix("registrations") {
pathEnd {
post {
entity(as[Registration]) { registration =>
Here is what is in build.sbt
scalaVersion := "2.10.4"
scalacOptions ++= Seq("-feature")
val akkaVersion = "2.3.8"
val sprayVersion = "1.3.1"
resolvers += "spray" at "http://repo.spray.io/"
resolvers += "Sonatype releases" at "https://oss.sonatype.org/content/repositories/releases"
// Main dependencies
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"com.typesafe.akka" %% "akka-camel" % akkaVersion,
"io.spray" % "spray-can" % sprayVersion,
"io.spray" % "spray-routing" % sprayVersion,
"io.spray" % "spray-client" % sprayVersion,
"io.spray" %% "spray-json" % sprayVersion,
"com.typesafe" % "config" % "1.2.1",
"org.apache.activemq" % "activemq-camel" % "5.8.0",
"ch.qos.logback" % "logback-classic" % "1.1.2",
"org.mongodb" %% "casbah" % "2.7.4"
)
Error:
12:33:03.477 [admcore-microservice-system-akka.actor.default-dispatcher-3] DEBUG s.can.server.HttpServerConnection - Dispatching POST request to http://localhost:8878/api/v1/adsregistrations to handler Actor[akka://admcore-microservice-system/system/IO-TCP/selectors/$a/1#-1156351415]
Uncaught error from thread [admcore-microservice-system-akka.actor.default-dispatcher-3] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[admcore-microservice-system]
java.lang.NoSuchMethodError: spray.json.JsonParser$.apply(Ljava/lang/String;)Lspray/json/JsValue;
at spray.httpx.SprayJsonSupport$$anonfun$sprayJsonUnmarshaller$1.applyOrElse(SprayJsonSupport.scala:36)
at spray.httpx.SprayJsonSupport$$anonfun$sprayJsonUnmarshaller$1.applyOrElse(SprayJsonSupport.scala:34)
To avoid any issue, I would define the Register class (which seems a data model) as follows
case class Register(system: String, identity: String, id: String)
That's because it makes more sense to me having an id field as String rather than as BSON ObjectId (I'm used to datamodel which don't depend on 3rd party libraries).
Therefore, the right SprayJson protocol would make use of jsonFormat3 rather than jsonFormat2:
object RegistrationProtocol extends DefaultJsonProtocol {
implicit val registrationFormat = jsonFormat3(Registration)
}
And that would solve any kind of JSON serialization issue.
Finally, your toBson and fromBson converters would be:
def toBson(r: Registration): DBObject = {
MongoDBObject(
"system" -> r.system,
"identity" -> r.identity,
"_id" -> new ObjectId(r.id)
)
}
and
def fromBson(o: DBObject): Registration = {
Registration(
system = o.as[String]("system"),
identity = o.as[String]("identity"),
id = o.as[ObjectId]("_id").toString
)
}
A that's where the BSON ObjectId is being used: much closer to the MongoDB dependant logic.
I need to make a simple HTTP request using spray framework. I found some examples on their web site but they turned out to be complicated and involving Akka which is not necessary for me.
Besides, I need to be able to fill in a request's headers (like X-Application, content-type, etc) and, of course, a requests's post data (in my case, it would be a data in JSON).
So how do I do that?
Here is an example. There is going to be a small amount of akka code but as I mentioned in my comment, it's necessary for spray:
import spray.httpx.RequestBuilding._
import spray.http._
import HttpMethods._
import HttpHeaders._
import MediaTypes._
import spray.json._
import DefaultJsonProtocol._
import spray.httpx.SprayJsonSupport._
import akka.actor.ActorSystem
import spray.io.IOExtension
import akka.actor.Props
import spray.can.client.HttpClient
import spray.client.HttpConduit
import scala.concurrent.Future
import scala.util.Failure
import scala.util.Success
case class MyObj(str:String, i:Int)
object SprayExample {
implicit val myObjFormat = jsonFormat2(MyObj)
def main(args: Array[String]) {
import concurrent.ExecutionContext.Implicits._
val obj = MyObj("hello", 1)
val req = Post("/some/url", obj) ~> addHeader("X-Foo", "bar")
implicit val system = ActorSystem()
val ioBridge = IOExtension(system).ioBridge()
val httpClient = system.actorOf(Props(new HttpClient(ioBridge)))
val conduit = system.actorOf(
props = Props(new HttpConduit(httpClient, "localhost", 8080)),
name = "http-conduit"
)
val pipeline = HttpConduit.sendReceive(conduit)
val response: Future[HttpResponse] = pipeline(req)
response onComplete{
case Failure(ex) => ex.printStackTrace()
case Success(resp) => println("success: " + resp.status)
}
}
}
My build file looks like this:
scalaVersion := "2.10.0"
resolvers ++= Seq(
"Scala Tools Repo Releases" at "http://scala-tools.org/repo-releases",
"Typesafe Repo Releases" at "http://repo.typesafe.com/typesafe/releases/",
"spray" at "http://repo.spray.io/"
)
libraryDependencies ++= Seq(
"io.spray" % "spray-httpx" % "1.1-M7",
"io.spray" % "spray-client" % "1.1-M7",
"com.typesafe.akka" %% "akka-actor" % "2.1.0",
"io.spray" %% "spray-json" % "1.2.5"
)