Spray: Marshalling UUID to JSON - json

I'm having some problems marshalling from UUID to JSON
def complete[T <: AnyRef](status: StatusCode, obj: T) = {
r.complete(status, obj) // Completes the Request with the T obj result!
}
^
The signature of my class:
trait PerRequest extends Actor
with Json4sSupport
with Directives
with UnrestrictedStash
with ActorLogging {
val json4sFormats = DefaultFormats.
This gives me :
"id": {
"mostSigBits": -5052114364077765000,
"leastSigBits": -7198432257767597000
},
instead of:
"id": "b9e348c0-cc7f-11e3-9c1a-0800200c9a66"
So, how can I add a UUID format to json4sFormats to marshall UUID's correctly?? In other cases I mix in with a trait that have this function:
implicit object UuidJsonFormat extends RootJsonFormat[UUID] {
def write(x: UUID) = JsString(x.toString)
def read(value: JsValue) = value match {
case JsString(x) => UUID.fromString(x)
case x => deserializationError("Expected UUID as JsString, but got " + x)
}
}
But here I'm not able to because I don't have declared a spray.json.RootJsonReader and/or spray.json.RootJsonWriter for every type T and does not compile. (See complete function T <: AnyRef)
Thanks.

I solved it! If someone has the same problem take a look here
I defined my own UUID Serializer as follows:
class UUIDSerializer extends CustomSerializer[UUID](format => (
{
case JObject(JField("mostSigBits", JInt(s)) :: JField("leastSigBits", JInt(e)) :: Nil) =>
new UUID(s.longValue, e.longValue)
},
{
case x: UUID => JObject(JField("id", JString(x.toString)))
}
))
And now it's working!

Related

Json body converted to sealed trait

I have a Play! endpoint which can receive a json body as 3 or 4 forms (I tried using generic type, but not working).
Controller:
def getChartData = Action.async(parse.json) { request =>
// ERROR: can not cast JsValue to type ChartDataRequest (which is sealed trait)
service.getChartData(request.body.asInstanceOf[ChartDataRequest]).map {
data => Ok(Json.toJson(data))
}.recover {
case _ => InternalServerErrror
}
}
Service:
def getChartData(request: ChartDataRequest): Future[data_type] = {
if (request.isInstanceOf[PieChartRequest]) {
//
} else if (request.isInstanceOf[BarChartRequest]) {
//
} else {
//
}
}
dtos:
sealed trait ChartDataRequest
final case class PieChartRequest(field1: String, field2: String, ....)
extends ChartDataRequest
final case class BarChartRequest(field1: String, field2: String, ....)
extends ChartDataRequest
I found here the solution to use sealed traits, but can't do it well.
In this point, I can not convert the JsValue to ChartDataRequest type. I can use a field "classType" in my json and then using the match pattern to create the specified object (PieDataRequest or BarDataRequest) but I think this is not the best solution.
Inside all my controller methods where I send objects as json body, I use the play validator, but have the same problem, and I removed it from code.
// ChartDataRequest can have PieDataRequest or BarDataRequest type
request.body.validate[ChartDataRequest] match {
case JsSuccess(value, _) => // call the service
case JsError(_) => Future(BadRequest("Invalid json body"))
}
thanks
You can follow this:
sealed trait ChartDataRequest
final case class PieChartRequest(field1: String) extends ChartDataRequest
final case class BarChartRequest(field2: String) extends ChartDataRequest
final case object WrongData extends ChartDataRequest
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val ChartDataRequests: Reads[ChartDataRequest] = {
val pc = Json.reads[PieChartRequest]
val bc = Json.reads[BarChartRequest]
__.read[PieChartRequest](pc).map(x => x: ChartDataRequest) |
__.read[BarChartRequest](bc).map(x => x: ChartDataRequest)
}
def getChartData(request: ChartDataRequest) = {
request match {
case _: PieChartRequest =>
Future("PieChartRequest")(defaultExecutionContext)
case _: BarChartRequest =>
Future("BarChartRequest")(defaultExecutionContext)
case _ =>
Future("WrongData")(defaultExecutionContext)
}
}
def getChartDataAction = Action.async(parse.json) { request =>
// you can separate this to a new function
val doIt = request.body.asOpt[JsObject].fold[ChartDataRequest](
WrongData
){
jsObj =>
jsObj.asOpt[ChartDataRequest].fold[ChartDataRequest](
WrongData
)(identity)
}
getChartData(doIt).map {
data => Ok(Json.toJson(data))
}(defaultExecutionContext).recover {
case _ => InternalServerError
}(defaultExecutionContext)
}

Play Framework 2.5 Serializing JSON Traits

I have a sealed trait that is like below:
sealed trait MyMessages
object MyMessages {
case object Init extends MyMessages
case object Destroy extends MyMessages
case class Tick(elem: Long) extends MyMessages
}
I have to now write a formatter for serializing and de-serializing this into to and from a JSON. This is what I came up with:
implicit object MyMessagesWrites extends Writes[MyMessages] {
def writes(myMessages: MyMessages): JsValue = myMessages match {
case Init => Json.toJson("INIT")
case Destroy => Json.toJson("DESTROY")
case tick: Tick => Json.toJson(Tick)
}
def reads(json: JsValue): MyMessages = {
// How do I get from JSValue to a MyMessages type???
}
}
Implementing the writes was easy, but how do I implement the reads?
Assuming you serialize the Tick instance as a bare JSON number, I would do it like so:
implicit object MyMessageReads extends Reads[MyMessages] {
def reads(json: JsValue) = json match {
case JsString("INIT") => JsSuccess(MyMessages.Init)
case JsString("DESTROY") => JsSuccess(MyMessages.Destroy)
case JsNumber(n) => JsSuccess(Tick(n.toLongExact))
case e => JsError(s"Invalid message: $e")
}
}
Note that you can also make the reads/writes a bit more succinct by using the more functional style:
implicit val myMessagesWrites = Writes[MyMessages] {
case Init => JsString("INIT")
case Destroy => JsString("DESTROY")
case Tick(n) => JsNumber(n)
}
implicit val myMessageReads = Reads[MyMessages] {
case JsString("INIT") => JsSuccess(MyMessages.Init)
case JsString("DESTROY") => JsSuccess(MyMessages.Destroy)
case JsNumber(n) => JsSuccess(Tick(n.toLongExact))
case e => JsError(s"Invalid message: $e")
}

How to marshal different response types in spray?

Consider an http service that can return two json as response:
successful
{
"yourField":"value"
}
failure
{
"errorCode": 3
}
To deal with these json's I need to create 2 case classes case class RespSucc(yourField:String) and
case class RespFail(errorCode:Int).
For now I have to to something like that:
//unmarshal is spray.httpx.ResponseTransformation#unmarshal
if (response.entity.asString.contains("errorCode")) {
unmarshal[RespSucc].apply(response)
}
else {
unmarshal[RespFail].apply(response)
}
Is there an api to parse these classes automatically without any if? E.g. can unmarshaller looks into json fields and select approriate case class?
spray-json supports Either which is a very useful data type for this kind of situations.
val data = unmarshal[Either[RespFail, RespSucc]].apply(response)
// You can match it
data match {
case Left(err) => handleError(err)
case Right(suc) => handleSuccess(suc)
}
// Or you can fold it (I prefer folding)
data.fold(err => handleError(err), suc => handleSuccess(suc))
You can try something like this:
trait Resp
case class RespSucc(yourField: String) extends Resp
case class RespFail(errorCode: Int) extends Resp
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object ColorJsonFormat extends RootJsonFormat[Resp] {
def write(r: Resp) = r match {
case s: RespSucc =>
JsObject("yourField" -> JsString(s.yourField))
case f: RespFail =>
JsObject("errorCode" -> JsNumber(f.errorCode))
}
def read(value: JsValue) = value.asJsObject.getFields("yourField", "errorCode") match {
case Seq(JsString(yourField)) => RespSucc(yourField)
case Seq(JsNumber(errorCode)) => RespFail(errorCode.intValue())
case _ => deserializationError("Resp expected")
}
}
}
import MyJsonProtocol._
unmarshal[Resp](entitySucc) //Right(RespSucc(abc))
unmarshal[Resp](entityFail) //Right(RespFail(3))

What should a Play framework implicit val Writes[T] look like for super type?

What do I put instead of ??? so the code will type check? Or is there something else I should be doing? I'm using Play to generate JSON for classes B, C, D that all extend A (Layer), but the code that tries to build the JSON only knows it has an A, not which subtype B, C or D.
class Layer
object Layer {
implicit val layerWrites = new Writes[Layer] {
def writes(x: Layer) = x match {
case a: CloudLayer => ???
case b: VerticalVisibility => ???
case c: SkyClear => ???
}
}
}
case class CloudLayer(coverage: String, base: Int) extends Layer
case class VerticalVisibility(height: Int) extends Layer
case class SkyClear() extends Layer
object CloudLayer {
implicit val cloudLayerWrites = new Writes[CloudLayer] {
def writes(x: CloudLayer) = Json.obj(
"layerType" -> "cloudLayer",
"coverage" -> x.cloudCoverage,
"base" -> x.base * 100
)
}
}
object VerticalVisibility {
implicit val verticalVisibilityWrites = new Writes[VerticalVisibility] {
def writes(x: VerticalVisibility) = Json.obj(
"layerType" -> "verticalVisibility",
"height" -> x.height * 100
)
}
}
object SkyClear {
implicit val skyClearWrites = new Writes[SkyClear] {
def writes(x: SkyClear) = Json.obj( "layerType" -> "skyClear" )
}
}
The easiest solution would be just to remove the implicit modifiers from the instances in the subclasses and then refer to them explicitly:
object Layer {
implicit val layerWrites = new Writes[Layer] {
def writes(x: Layer) = x match {
case a: CloudLayer => CloudLayer.cloudLayerWrites.writes(a)
case b: VerticalVisibility =>
VerticalVisibility.verticalVisibilityWrites.writes(b)
case c: SkyClear => SkyClear.skyClearWrites.writes(c)
}
}
}
You could also just scrap the individual instances and move their contents into the pattern match.
If you're feeling adventurous, Julien Richard-Foy has a pretty neat enhanced version of the Json.writes, etc. macros that works on sealed type hierarchies.

Scala, Akka, Spray: How to validate json data before processing?

I can process this json when all the inputs are valid, i.e with valid keys (including case) and values. The next step is to validate keys and return 400 (Bad Request) if the keys or values are invalid. What is a good way to add this validation?
The API call
POST http://localhost:8080/api/v1/adsession
Content-Type: application/json
body {
"sessionId": "abcd123123123",
"serviceGroup": "1234",
"targetCode": {"zipcodes":"30096,30188","code2":"value2"}
}
Route handler
class AdSessionRoutes(services: Services)(implicit ec: ExecutionContext, log: LoggingContext) extends ApiRoute(services) {
implicit val timeout = Timeout(10 seconds)
val postSession = pathPrefix("adsession") & pathEnd & post
val route: Route = {
withService("adSession") { service =>
postSession {
entity(as[AdSession]) { adSession =>
log.info(s"Processing POST ${adSession}")
val future = (service ? CreateAdSession(adSession)).mapTo[AdSession]
onComplete(future) {
case Success(result) =>
complete(StatusCodes.Created, result)
case Failure(e) =>
log.error(s"Error: ${e.toString}")
complete(StatusCodes.InternalServerError, Message(ApiMessages.UnknownException))
}
}
}
}
}
}
Model object
case class AdSession(
sessionId: String,
serviceGroup: String,
targetCodes: Map[String,String],
id: Option[String] = None)
object AdSessionJsonProtocol extends DefaultJsonProtocol {
implicit val adSessionFormat = jsonFormat4(AdSession)
}
entity(as[AdSession]) does map keys to case class fields, but I'm not sure how to catch those errors. I would like to capture those errors as well as add additional validations and return 400 with valid json error response.
I know this may be a little late but since akka-http-2.4.6, you can put the verification logic inside a case class. Check out this for info on how to do it.
Define your own read and write methods for AdSession like this:
object AdSessionJsonProtocol {
implicit object adSessionJsonFormat extends RootJsonFormat[AdSession] {
override def read(json: JsValue): AdSession = ???
override def write(obj: AdSession): JsValue = ???
}
}
Inside read function you can perform additional validation.
Another question is how to pass collected errors to Spray route. I would like to suggest you to wrap AdSession into Either[String, AdSession], for example:
postSession {
entity(as[Either[String, AdSession]]) { adSession =>
So, now your adSessionJsonFormat will looks like:
implicit object adSessionJsonFormat extends RootJsonFormat[Either[String, AdSession]] {
override def read(json: JsValue): Either[String, AdSession] = {
// json.asJsObject.fields access fields, perform checks
// if everything is OK return Right(AdSession(...))
// otherwise return Lift("Error Message")
}
override def write(obj: Either[String, AdSession]): JsValue = ???
}
But, I think it's possible to solve it in more elegant way using some implicit magic.