Handle JSON data in POST request using json4s - json

I'm trying to map JSON i/p to my case class CacheRequest.
request is POST.
I'm new to Scala and Akka.
import org.json4s.{DefaultFormats, Formats}
implicit val formats: Formats = DefaultFormats
val route: Route = traceContextAwareRoute {
pathPrefix(system.name) {
post {
path("sample") {
entity(as[CacheRequest]) { x => {
val cacheRequest: CacheRequest = CacheRequest(x.a, x.b, x.c, x.d, x.e, x.f)
onComplete(getSystemStock(cacheRequest)) {
(response: Try[Option[CacheResponse]]) => complete(processResponse(response))
}
}
}
}
}
}
My case class is like this.
case class CacheRequest(a: String,
b: String,
c: Int,
d: Int,
e: Int,
f: Int)
Getting an Error Like
could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[mcc.movie.common.model.CacheRequest]
not enough arguments for method as: (implicit um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[mcc.movie.common.model.CacheRequest])akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[mcc.movie.common.model.CacheRequest]
I'supposed to do this using json4s.
Any help regarding this is fine for me.

You can use this lib: https://github.com/hseeberger/akka-http-json
code may something like this
import org.json4s.{DefaultFormats, Formats, jackson}
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
class Route {
import Json4sSupport._
implicit val formats: Formats = DefaultFormats
implicit val serialization = jackson.Serialization
val route: Route = traceContextAwareRoute {
pathPrefix(system.name) {
post {
path("sample") {
entity(as[CacheRequest]) { x => {
val cacheRequest: CacheRequest = CacheRequest(x.a, x.b, x.c, x.d, x.e, x.f)
onComplete(getSystemStock(cacheRequest)) {
(response: Try[Option[CacheResponse]]) => complete(processResponse(response))
}
}
}
}
}
}
}

Related

Accept all JSON from the request AKKA-HTTTP

I have one route:
val receiveMessage = post {
path("receive_message"){
entity(as[Receive]) {
message => {
println(message)
complete("ok")
}
}
}
}
He work with case class:
// spray (JSON marshalling)
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import spray.json.DefaultJsonProtocol._
final case class Receive(notification_text: String)
// formats for unmarshalling and marshalling
implicit val itemFormat4 = jsonFormat1(Receive)
Is there a way to make a route that can receive any JSON objects?
You can type your route to receive a JsObject (from SprayJson):
entity(as[JsObject])

Play Json Reads nested generic serialized Json

Consider the following JSON
{
"a": "{\"b\": 12, \"c\": \"test\"}"
}
I would like to define a generic reads Reads[Outer[T]] for this kind of serialized Json
import play.api.libs.json.{Json, Reads, __}
final case class Outer[T](inner: T)
final case class SpecializedInner(b: Int, c: String)
object SpecializedInner {
implicit val reads: Reads[SpecializedInner] = Json.reads[SpecializedInner]
}
object Outer {
implicit def reads[T](implicit readsT: Reads[T]): Reads[Outer[T]] = ???
}
How can I achieve my goal? I tried to flatMap a string-reads for the field "outer.a" but got stuck since I am not able to produce a Reads[T] from the validated JSON
object Outer {
implicit def reads[T](implicit readsT: Reads[T]): Reads[Outer[T]] =
(__ \ "a").read[String].flatMap(x => Json.parse(x).validate[T])
}
You need just add map inside Outer.reads construction after validate[T] invocation.
Please, see next code for example:
object App {
final case class Outer[T](inner: T)
object Outer {
implicit def reads[T](implicit innerReads: Reads[T]): Reads[Outer[T]] = { json: JsValue =>
json.validate[String].flatMap(string => Json.parse(string).validate[T].map(Outer.apply[T]))
}
}
final case class Root[T](a: Outer[T])
object Root {
implicit def reads[T](implicit innerReads: Reads[T]): Reads[Root[T]] = Json.reads
}
final case class SpecializedInner(b: Int, c: String)
object SpecializedInner {
implicit val reads: Reads[SpecializedInner] = Json.reads
}
def main(args: Array[String]): Unit = {
val rawJson = "{\"a\": \"{\\\"b\\\": 12, \\\"c\\\": \\\"test\\\"}\"}"
println(Json.parse(rawJson).validate[Root[SpecializedInner]])
}
}
Which produced next result in my case:
JsSuccess(Root(Outer(SpecializedInner(12,test))),)
Hope this helps!

How to edit existing JSON object with sprayJSON

I am using akka with spray json support for which I need to edit value in the recieved json.
import akka.http.scaladsl.server.Directives
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json._
final case class Item(name: String, id: Long)
final case class Order(items: List[Item],orderTag:String)
trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
implicit val itemFormat = jsonFormat2(Item)
implicit val orderFormat = jsonFormat2(Order)
}
In my use case I recieve the json with orderTag value as null, all I need to do is edit the orderTag value with and then use it as entity value.Is it possible to write/edit jsonObject and How to do that ?
class MyJsonService extends Directives with JsonSupport {
// format: OFF
val route =
get {
pathSingleSlash {
complete(Item("thing", 42)) // will render as JSON
}
} ~
post {
entity(as[Order]) { order => // will unmarshal JSON to Order
val itemsCount = order.items.size
val itemNames = order.items.map(_.name).mkString(", ")
complete(s"Ordered $itemsCount items: $itemNames")
}
}
}
You can just edit the json AST like ..
val json = """{"orderTag":null}"""
val jsVal = json.parseJson
val updatedJs = if (jsObj.fields.get("orderTag") == Some(JsNull)) {
JsObject(jsObj.fields + ("orderTag" -> JsString("new tag")))
} else {
jsObj
}
updatedJs.compactPrint
res26: String = """
{"orderTag":"new tag"}
"""

spray-json: could not find implicit value for FromRequestUnmarshallar

I'm creating my first application with AKKA-http. I'm currently using spray-json to write object to json. When I created GET request everything is working fine, but I tried a POST request and the following error shows:
Error:(55, 26) could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[nl.quintor.model.Employee]
entity(as[Employee]) { request =>
This is my main class:
object Main extends App with Routes {
implicit val system = ActorSystem("system")
implicit val executor: ExecutionContext = system.dispatcher
implicit val materializer: ActorMaterializer = ActorMaterializer()
override val slickboard = createSlickboard
Http().bindAndHandle(routes, httpInterface, httpPort)
private def createSlickboard: ActorRef =
system.actorOf(Slickboard.props(system), applicationName)
}
}
I defined my routes in the following trait:
trait Routes extends CORSSupport with Protocols {
val slickboard: ActorRef
implicit val timeout = Timeout(5 seconds)
val routes =
corsHandler {
pathPrefix("employees") {
pathEnd {
get {
complete {
(slickboard ? GetAllEmployees).mapTo[HttpResponse]
}
}
}
} ~
pathPrefix("employee") {
path(IntNumber) { id =>
get {
complete {
(slickboard ? GetEmployeeById(id)).mapTo[HttpResponse]
}
} ~
post {
decodeRequest {
entity(as[Employee]) { request =>
complete {
request.toString()
}
}
}
}
}
}
}
}
I defined my protocol just like spray recommended:
trait Protocols extends DefaultJsonProtocol {
implicit val employeeFormat = jsonFormat8(Employee.apply)
}
The class I'm trying to convert to json is the following:
case class Employee(ID: Int, firstname: String, lastname: String, street: String, zipCode: String, city: String, phoneNumber: String, image: String)
I tried multiple solutions found in stack overflow, but none of them are actually working.
Could someone please help me with this problem?
I suspect you need
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._

Implicit parameters not found

I'm having some trouble figuring out why compiler complains about not finding an implicit parameter for reads because I'm almost sure that it is in the scope. The error is the following:
Error:(13, 18) No Json deserializer found for type Config. Try to implement an implicit Reads or Format for this type.
test.validate[Config].map {
^
Error:(13, 18) not enough arguments for method validate: (implicit rds: play.api.libs.json.Reads[Config])play.api.libs.json.JsResult[wings.m2m.conf.model.Config].
Unspecified value parameter rds.
test.validate[Config].map {
^
and it happens in the following code:
import play.api.libs.json._
import play.api.libs.json.Reads._
import Config.JsonImplicits._
import scala.util.Try
object Test {
def main(args: Array[String]) {
val test = Json.obj("action" -> Config.Action.nameAcquisitionRequest.toString, "value" -> "hola")
test.validate[Config].map {
t => println(t)
t
}
}
}
/**
* Config companion object
*/
object Config {
type ValueType = String
val ActionKey = "action"
val ValueKey = "value"
object Action extends Enumeration {
type Action = Value
val nameAcquisitionRequest = Value("nameAcquisitionRequest")
val nameAcquisitionReject = Value("nameAcquisitionReject")
val nameAcquisitionAck = Value("nameAcquisitionAck")
val broadcast = Value("broadcast")
}
/**
* Json implicit conversions
*/
object JsonImplicits {
implicit object ConfigReads extends Reads[Config] {
def hTypeCast(action: Config.Action.Value, value: Config.ValueType): Config = {
action match {
case Config.Action.nameAcquisitionRequest => NameAcquisitionRequest(value)
case Config.Action.nameAcquisitionReject => NameAcquisitionReject(value)
case Config.Action.nameAcquisitionAck => NameAcquisitionAck(value)
}
}
override def reads(json: JsValue): JsResult[Config] = json match {
case json: JsObject =>
val action = (json \ ActionKey).as[String]
Try(Config.Action.withName(action)) map {
a =>
val value = (json \ ValueKey).as[String]
JsSuccess(hTypeCast(a, value))
} getOrElse (JsError("Can't convert to Config"))
case _ => JsError("Can't convert to Config")
}
}
implicit object ConfigWrites extends OWrites[Config] {
def jsObjectCreator(action: Config.Action.Value, value: Config.ValueType): JsObject = {
Json.obj(ActionKey -> action.toString, ValueKey -> Json.toJson(value))
}
override def writes(o: Config): JsObject = o match {
case c: NameAcquisitionRequest => jsObjectCreator(Config.Action.nameAcquisitionRequest, c.value)
case c: NameAcquisitionReject => jsObjectCreator(Config.Action.nameAcquisitionReject, c.value)
case c: NameAcquisitionAck => jsObjectCreator(Config.Action.nameAcquisitionAck, c.value)
}
}
}
}
sealed trait Config {
val value: Config.ValueType
}
/**
* Intermediate config message
* #param value
*/
case class NameAcquisitionRequest(override val value: String)
extends Config
case class NameAcquisitionReject(override val value: String)
extends Config
case class NameAcquisitionAck(override val value: String)
extends Config
case class Broadcast(override val value: String)
extends Config
the error occurs when executing the main method on the Test object. To make this example work, make sure to add the following dependency in the SBT: "com.typesafe.play" %% "play-json" % "2.4.1" . And I'm not sure, but maybe this resolver is needed: resolvers += "Typesafe Repo" at "http://repo.typesafe.com/typesafe/releases/"
I am not sure what you are trying to achieve and whether this solves your problem but here you go:
test.validate[Config](Config.JsonImplicits.ConfigReads).map {
t => println(t)
t
}