The spray-json library extends basic Scala types with a toJson method. I'd like to convert an Any into a JsValue if there is such a pimp for the underlying type. My best attempt works, but is verbose:
import cc.spray._
val maybeJson1: PartialFunction[Any, JsValue] = {
case x: BigDecimal => x.toJson
case x: BigInt => x.toJson
case x: Boolean => x.toJson
case x: Byte => x.toJson
case x: Char => x.toJson
case x: Double => x.toJson
case x: Float => x.toJson
case x: Int => x.toJson
case x: Long => x.toJson
case x: Short => x.toJson
case x: String => x.toJson
case x: Symbol => x.toJson
case x: Unit => x.toJson
}
Ideally, I'd prefer something (impossible) like this:
def maybeJson2(any: Any): Option[JsValue] = {
if (pimpExistsFor(any))
Some(any.toJson)
else
None
}
Is there a way to do this without enumerating every type that has been enriched?
There is a way, but it requires a lot of reflection and therefore is quite a headache. The basic idea is as follows. The DefaultJsonProtocol object inherits a bunch of traits that contain implicit objects which contain write methods. Each of those will have an accessor function, but you won't know what it's called. Basically, you'll just take all methods that take no parameters and return one object that has a write method that takes the class of your object and returns a JsValue. If you find exactly one such method that returns one such class, use reflection to call it. Otherwise, bail.
It would look something like this (warning, untested):
def canWriteMe(writer: java.lang.Class[_], me: java.lang.Class[_]):
Option[java.lang.reflect.Method] =
{
writer.getMethods.find(_.getName == "write").filter{ m =>
classOf[JsValue].isAssignableFrom(m.getReturnType) && {
val parm = m.getParameterTypes()
m.length == 1 && parm(0).isAssignableFrom(me)
}
}
}
def maybeJson2(any: Any): Option[JsValue] = {
val couldWork = {
DefaultJsonProtocol.getClass.getMethods.
filter(_.getParameterTypes.length==0).
flatMap(m => canWriteMe(m.getReturnType, any.getClass).map(_ -> m))
}
if (couldWork.length != 1) None else {
couldWork.headOption.map{ case (wrMeth, obMeth) =>
val wrObj = obMeth.invoke(DefaultJsonProtocol)
val answer = wrMeth.invoke(wrObj, any)
}
}
}
Anyway, you're best off pulling the DefaultJsonProtocol class apart in the REPL step by step and finding out how to reliably identify the objects that define the writers, and then get the write methods out of them.
I'm not sure it will fit you needs, but here is an alternative approach wich is really simple and type-safe.
If you kept the type of the argument (instead of using Any) you could rely on implicit parameter resolution to find the correct conversion at compile time:
def toJson[T:JsonFormat]( t: T ): JsValue = implicitly[JsonFormat[T]].write(t)
You won't need an option, because the program will fail at compile time if you try to pass an argument which is not "pimpable".
Related
I would like to know that how can I return json response data from Play(2.2.x) Scala controller class to display on my view page ? I have json objects in Postgresql database(table name: "test" and having: id and name). Please provide me any solutions for it.
I have tried the following cases(a and b), but I am not sure why I am not getting the response(like: names) on my controller, so I can show them on my view page ? since I am very new to Play/Scala and Postgresql.
case a. If I give like:
model:
def getTestValuesFromTable() = {
DB.withConnection { implicit connection =>
val selectJson =SQL("select name from test").on().apply().collect {
case Row(id:Long, Some(name:String)) =>
new TestContent(name)
}
//.head
//JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)
}
}
controller:
def getTest = Action {
val response = TestContent.getTestValuesFromTable()
Ok("Done")
//Ok(response)
}
Output is: Done(application is executing fine without any exceptions, of course json data is not coming since I am returning: Done only, so getting output: "Done")
case b. If I do like this: getting error: not enough arguments for method apply: (n: Int)models.Testname in trait LinearSeqOptimized. Unspecified value parameter n. I really not sure how can I get my response for it ?
controller:
def getTest = Action {
val response = TestContent.getTestValuesFromTable()
// Ok("Done")
Ok(response)
}
model:
def getTestValuesFromTable(): JsValue = {
DB.withConnection { implicit connection =>
val selectJson = SQL("select * from test")
JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)
// val selectJson =SQL("select name from test").on().apply().collect {
// case Row(id:Long, Some(name:String)) =>
// new TestContent(name)
// }
//.head
JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)//not enough arguments for method apply: (n: Int)models.Testname in trait LinearSeqOptimized. Unspecified value parameter n.
}
}
Please let me know how to get my response ?
getJsonValuesFromTable method return nothing (Unit). To fix it change definition of this method to
def getJsonValuesFromTable(testContent: TestContent) = {
or explicitly setting type:
def getJsonValuesFromTable(testContent: TestContent): Unit = {
Also as a next step to let client know that you are returning json, you should set content type:
Ok(Json.obj(response)).as("application/json")
Is it possible, in Scala, to define a function that would receive any other function as a parameter?
It should be something like the following:
object Module extends SecureModule{
val bc = new MyBC()
def method(parameter: Type) = {
exec(bc.method(parameter))
}
def method2(parameter1: Type1, parameter2: Type2) = {
exec(bc.method2(parameter1,parameter2))
}
}
trait SecureModule {
def exec(f: ANY_PARAMETER => ANY_RESULT) = {
//some extra processing
f
}
}
is it possible? If so, how could I achieve this?
Thank you in advance.
The nice thing about scala is that you can create what seems to be your own syntax.
If what you want to do is wrap an operation so that you can do pre and post processing, as well as control the execution context, then you do this by using call-by-name parameters. For example, if we just wanted to time how long a block of code takes, then we could do something like this:
def timer[T](block: => T): (T,Long) = {
val startDate = new Date()
val result = block
val endDate = new Date()
(result, endDate.getTime()-startDate.getTime())
}
We can use it like this:
val (result,duration) = timer {
1+3
}
Or like this
val (result,duration) = timer {
"hello" + " world!"
}
And the result will have the correct type from the block that you pass in while also giving you the duration that you expect.
I am under the impression that your description is somewhat misleading.
The way I understand it, what you (might) want to do is delaying the execution of the bc.method calls until some other code has been performed.
If so, try this:
object Module extends SecureModule{
val bc = new MyBC()
def method(parameter: Type) = {
exec(() => bc.method(parameter))
}
def method2(parameter1: Type1, parameter2: Type2) = {
exec(() => bc.method2(parameter1,parameter2))
}
}
trait SecureModule {
def exec[Result](f: () => Result): Result = {
//some extra processing
f()
}
}
You can't take any function as a parameter. What would you even do it?
At best, you can take any function that has a specific number of parameters.
For example, here, f takes one argument and returns a value.
def exec[A,B](f: A => B)
And here, f takes two arguments:
def exec[A,B,C](f: (A, B) => C)
If you don't care about the return type of the function, you could always use Any instead of a type parameter, since functions are covariant in their return type:
def exec[A](f: A => Any)
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.
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!
I've got a function which loads various models, and currently have this kind of setup:
if(message == "user") {
var model = User.findAll(
("room" -> "demo")
)
} else if (message == "chat") {
var model = Chat.findAll(
("room" -> "demo")
)
}
This is really clunky as I aim to add lots more models in future, I know in javascript you can do something like this:
var models = {
"user" : load_user,
"chat" : load_chat
}
Where "load_user" and "load_chat" would load the respective models, and so I can streamline the whole thing by doing:
var model = models[message]();
Is there a way I can do something similar in Scala, so I can have a simple function which just passes the "message" var to a List or Object of some kind to return the relevant data?
Thanks in advance for any help, much appreciated :)
In Scala you can do:
val model = message match {
case "user" => loadUser() // custom function
case "chat" => loadChat() // another custom function
case _ => handleFailure()
}
You can as well work with a Map like you did in your JavaScript example like so:
scala> def loadUser() = 1 // custom function
loadUser: Int
scala> def loadChat() = 2 // another custom function
loadChat: Int
scala> val foo = Map("user" -> loadUser _, "chat" -> loadChat _)
foo: scala.collection.immutable.Map[java.lang.String,() => Int] = Map(user -> <function0>, chat -> <function0>)
scala> foo("user")()
res1: Int = 1
Pay attention to the use of "_" in order to prevent evaluation of loadUser or loadChat when creating the map.
Personally, I'd stick with pattern matching.