Error working with JSON in Play 2 - json

I am trying to serialize / deserialize using JSON and the Play Framework 2.1.0 and Scala 2.10. I am using Anorm, and I have a pretty simple Object that I want to store in a database. The Order is very simple:
case class Order(id: Pk[Long] = NotAssigned, mfg: String, tp: String)
In my controller, I am trying to build a REST interface to be able to accept and send an Order instance (above) as JSON. In there, I have the following code:
implicit object PkFormat extends Format[Pk[Long]] {
def reads(json: JsValue):Pk[Long] = Id(json.as[Long])
def writes(id: Pk[Long]):JsNumber = JsNumber(id.get)
}
However, this is failing to compile when I run "play test" with the following:
overriding method reads in trait Reads of type (json: play.api.libs.json.JsValue)play.api.libs.json.JsResult[anorm.Pk[Long]];
[error] method reads has incompatible type
[error] def reads(json: JsValue):Pk[Long] = Id(json.as[Long])
Does anyone know why this is happening?
I have a lot of experience with JAXB, but I am realtively new to Play and Scala, and I haven't been able to find any answers so far. This seems like a pretty simple use case, actually I was hoping there would be a simpler solution (like Annotations), but I wasn't able to find one (at least not yet)
Any help is greatly appreciated!
Thanks

play.api.libs.json.Reads trait defines reads method as:
def reads(json : play.api.libs.json.JsValue) : play.api.libs.json.JsResult[A]
Therefore, the response of reads method is expected to be JsResult[A], not A; that is, JsResult[Pk[Long]] and not Pk[Long]. In a case of success, you'll want to return this:
implicit object PkFormat extends Format[Pk[Long]] {
def reads(json: JsValue):JsResult[Pk[Long]] = JsSuccess(Id(json.as[Long]))
def writes(id: Pk[Long]):JsNumber = JsNumber(id.get)
}

Related

Extending AutoDerivation in Circe does not work

My question concerns the second solution offered by mixel here: Scala Circe with generics
Note that the trait named Auto in Circe has been renamed to AutoDerivation in the current version of Circe.
I am using the solution mixel provides in his StackOverflow solution but have not been able to get it to work. I have tried things like updating my Circe version to the most recent one and making sure the Macro Paradise plugin is imported, but still no luck.
Here is my code. The first is its own file, called CirceGeneric.
import io.circe._
import io.circe.parser._
import io.circe.generic.extras._
object CirceGeneric {
trait JsonEncoder[T] {
def apply(in: T): Json
}
trait JsonDecoder[T] {
def apply(s: String): Either[Error, T]
}
object CirceEncoderProvider {
def apply[T: Encoder]: JsonEncoder[T] = new JsonEncoder[T] {
def apply(in: T) = Encoder[T].apply(in)
}
}
object CirceDecoderProvider {
def apply[T: Decoder]: JsonDecoder[T] = new JsonDecoder[T] {
def apply(s: String) = decode[T](s)
}
}
}
object Generic extends AutoDerivation {
import CirceGeneric._
implicit def encoder[T: Encoder]: JsonEncoder[T] = CirceEncoderProvider[T]
implicit def decoder[T: Decoder]: JsonDecoder[T] = CirceDecoderProvider[T]
}
The second is a method for unit testing that uses the Akka function responseAs. The method appears in a class called BaseServiceTest.
def responseTo[T]: T = {
def response(s: String)(implicit d: JsonDecoder[T]) = {
d.apply(responseAs[String]) match {
case Right(value) => value
case Left(error) => throw new IllegalArgumentException(error.fillInStackTrace)
}
}
response(responseAs[String])
}
The idea is to convert the result of responseAs[String] (which returns a string) into a decoded case class.
The code is not behaving as expected. Intellij does not detect any missing implicits, but when compilation time comes around, I am getting problems. I should mention that the BaseServiceTest file contains imports for CirceGeneric._ and Generic._, so a missing import statement is not the problem.
[error] [...]/BaseServiceTest.scala:59: could not find implicit value for parameter d: [...]CirceGeneric.JsonDecoder[T]
[error] response(responseAs[String])
Either the implicit conversion from Decoder[T] to JsonDecoder[T] is not happening, or the Decoder[T] instance is not being created. Either way, something is wrong.
You still need a Decoder or JsonDecoder context bound on responseTo.
def responseTo[T : Decoder]: T = ...
This is because all your code, and indeed mixel's code in the linked answer, is about abstracting from a Decoder out to a JsonDecoder trait which can be used for cross-library support. But you still don't have any way of constructing one without an underlying Decoder instance.
Now, there are some ways of automatically generating Decoders for (for instance) case classes contained in circe.generics.auto, but at this point in your code
def responseTo[T]: T = {
def response(s: String)(implicit d: JsonDecoder[T]) = ...
...
}
you're asking the compiler to be able to provide an implicit JsonDecoder (i.e., in your setup, Decoder) instance for any arbitrary type. As the accepted answer to the linked question explains, that's not possible.
You need to delay the implicit resolution to the point where you know what type you're dealing with - in particular, that you can provide a Decoder[T] instance for it.
EDIT: In your response to your comment regarding what the point is if you can't create JsonDecoders for all types...
My understanding of the linked question is that they're trying to abstract away the circe library in order to allow swapping out the JSON library implementation. This is done as follows:
add the JsonDecoder type class
have a package json which contains implicits (using Circe) for constructing them automatically via the package object extending AutoDerivation
have external code only refer to JsonDecoder and import the implicits in the json package
Then all the JSON serialization and implicit resolution works out without ever needing the calling code to reference io.circe, and it's easy to switch over the json/JsonDecoder to another JSON library if desired. But you're still going to have to use the JsonDecoder context bound, and be restricted to working with types where such an implicit can be constructed. Which is not every type.

How to add a custom marshaller to akka http?

As a beginner to both scala and akka-http, I am trying to hook into the serialization aka marshalling process.
The project uses akka#2.5.2 and akka-http#10.0.10". Furthermore, it has the akka-http-spray-json dependency included.
In the codebase, we use Java.Util.Currency (It may be deprecated which is not important as I'd still like to know how to add a custom marshaller.)
Given this example controller:
def getCurrencyExample: Route = {
path("currencyExample") {
val currency: Currency = Currency.getInstance("EUR")
val code: String = currency.getCurrencyCode
val shouldBeFormated = collection.immutable.HashMap(
"currencyCode" -> code,
"currencyObject" -> currency
)
complete(shouldBeFormated)
}
}
I get a response like this where the currency object becomes empty:
{
currencyObject: { },
currencyCode: "EUR",
}
I expect something like:
{
currencyObject: "EUR",
currencyCode: "EUR",
}
The currency object should be transformed into a JSON string. And since I do not want to transform each response manually, I want to hook into marshalling process and have it done in the background.
I want to add a custom marhaller only for Java.Util.Currency objects, yet even reading up on the docs I am very unsure how to proceed.
There are multiple approaches described, and I am not sure which fits my need, or where to start.
I tried creating my own CurrencyJsonProtocol:
package com.foo.api.marshallers
import java.util.Currency
import spray.json.{DefaultJsonProtocol, JsString, JsValue, RootJsonFormat}
object CurrencyJsonProtocol extends DefaultJsonProtocol {
implicit object CurrencyJsonFormat extends RootJsonFormat[Currency] {
override def read(json: JsValue): Currency = {
Currency.getInstance(json.toString)
}
override def write(obj: Currency): JsValue = {
JsString(obj.getCurrencyCode)
}
}
}
yet the mere existence of the file breaks my project:
[error] RouteDefinitons.scala:90:16: type mismatch;
[error] found : scala.collection.immutable.HashMap[String,java.io.Serializable]
[error] required: akka.http.scaladsl.marshalling.ToResponseMarshallable
[error] complete(shouldBeFormated)
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
and I have no idea why. (It was crashing due to my package name being called marshaller. That completely broke the compilation of the project.
From what I understand, you have all the pieces, you just need to put them together.
Spray json provides support for marshalling common types, such as Int, String, Boolean, List, Map, etc. But it doesn't know how to marshall 'Currency'. And to solve that you have created a custom marshaller for 'Currency' objects. You just need to plug it in the right place. All that you have to do is import the marshaller from your CurrencyJsonProtocol into your Controller like below:
import CurrencyJsonProtocol._
Also make sure you have the below imports as well:
import spray.httpx.SprayJsonSupport._
import spray.json.DefaultJsonProtocol._
And spray-json should automatically pick that up.
To understand how it works, you need to understand about implicits in scala. Although it looks like magic when you come from java-world like me, I can assure you it's not.
I´m using Spray support for years. Maybe i should try another one. Anyway, in Spray, a custom formatter for a type is simple. An example of the squants.market.currency https://github.com/typelevel/squants
import squants.market.{ Currency, Money }
import squants.market._
trait MoneyJsonProtocol extends DefaultJsonProtocol {
implicit object CurJsonFormat extends JsonFormat[Currency] {
def write(currency: Currency): JsValue = JsString(currency.code)
def read(value: JsValue): Currency = value match {
case JsString(currency) =>
defaultCurrencyMap.get(currency) match {
case Some(c) => c
case None => // throw an exception, for example
}
case _ => // throw another exception, for example
}
}
implicit def toJavaBigDecimal(b: math.BigDecimal): java.math.BigDecimal = b.underlying
}
Do you have a JsonFormat[Currency]? If yes, just fix it...
If not, you should follow the doc and create:
as domain model, specific result type to hold the answer e.g. case class CurrencyResult(currency: Currency, code: String)
in trait JsonSupport: a JsonFormat[Currency]
in trait JsonSupport: a JsonFormat[CurrencyResult]
As an alternative to point 1 above, you can probably type your map as: Map[String, JsValue].

No instance of play.api.libs.json.Format is available for models.AccountStatus in the implicit scope

No instance of play.api.libs.json.Format is available for models.AccountStatus in the implicit scope.
This is the code taken from a github page, and only class names and variable names are changed.
package models
import slick.jdbc.H2Profile._
import play.api.libs.json._
case class Account(id: Long, name: String, category: Int, status:AccountStatus)
object Account {
implicit val accountFormat = Json.format[Account]
}
sealed abstract class AccountStatus(val as:Int)
object AccountStatus{
final case object Draft extends AccountStatus(0)
final case object Active extends AccountStatus(1)
final case object Blocked extends AccountStatus(2)
final case object Defaulter extends AccountStatus(3)
implicit val columnType: BaseColumnType[AccountStatus] = MappedColumnType.base[AccountStatus,Int](AccountStatus.toInt, AccountStatus.fromInt)
private def toInt(as:AccountStatus):Int = as match {
case Draft => 0
case Active => 1
case Blocked => 2
case Defaulter => 3
}
private def fromInt(as: Int): AccountStatus = as match {
case 0 => Draft
case 1 => Active
case 2 => Blocked
case 3 => Defaulter
_ => sys.error("Out of bound AccountStatus Value.")
}
}
https://github.com/playframework/play-scala-slick-example/blob/2.6.x/app/models/Person.scala
So, this code needs to be added inside of the object AccountStatus code block since we need to use fromInt to transform an Int to an AccountStatus. This is a Reads defined for AccountStatus:
implicit object AccountStatusReads extends Reads[AccountStatus] {
def reads(jsValue: JsValue): JsResult[AccountStatus] = {
(jsValue \ "as").validate[Int].map(fromInt)
}
}
What's a Reads? It's just a trait that defines how a JsValue (the play class encapsulating JSON values) should be deserialized from JSON to some type. The trait only requires one method to be implemented, a reads method which takes in some json and returns a JsResult of some type. So you can see in the above code that we have a Reads that will look for a field in JSON called as and try to read it as an integer. From there, it will then transform it into an AccountStatus using the already defined fromInt method. So for example in the scala console you could do this:
import play.api.libs.json._
// import wherever account status is and the above reader
scala> Json.parse("""{"as":1}""").as[AccountStatus]
res0: AccountStatus = Active
This reader isn't perfect though, mainly because it's not handling the error your code will give you on out of bound numbers:
scala> Json.parse("""{"as":20}""").as[AccountStatus]
java.lang.RuntimeException: Out of bound AccountStatus Value.
at scala.sys.package$.error(package.scala:27)
at AccountStatus$.fromInt(<console>:42)
at AccountStatusReads$$anonfun$reads$1.apply(<console>:27)
at AccountStatusReads$$anonfun$reads$1.apply(<console>:27)
at play.api.libs.json.JsResult$class.map(JsResult.scala:81)
at play.api.libs.json.JsSuccess.map(JsResult.scala:9)
at AccountStatusReads$.reads(<console>:27)
at play.api.libs.json.JsValue$class.as(JsValue.scala:65)
at play.api.libs.json.JsObject.as(JsValue.scala:166)
... 42 elided
You could handle this by making the Reads handle the error. I can show you how if you want, but first the other part of a Format is a Writes. This trait, unsurprisingly is similar to reads except it does the reverse. You're taking your class AccountStatus and creating a JsValue (JSON). So, you just have to implement the writes method.
implicit object AccountStatusWrites extends Writes[AccountStatus] {
def writes(as: AccountStatus): JsValue = {
JsObject(Seq("as" -> JsNumber(as.as)))
}
}
Then this can be used to serialize that class to JSON like so:
scala> Json.toJson(Draft)
res4: play.api.libs.json.JsValue = {"as":0}
Now, this is actually enough to get your error to go away. Why? Because Json.format[Account] is doing all the work we just did for you! But for Account. It can do this because it's a case class and has less than 22 fields. Also every field for Account has a way to be converted to and from JSON (via a Reads and Writes). Your error message was showing that Account could not have a format automatically created for it because part of it (status field) had no formatter.
Now, why do you have to do this? Because AccountStatus is not a case class, so you can't call Json.format[AccountStatus] on it. And because the subclasses of it are each objects, which have no unapply method defined for them since they're not case classes. So you have to explain to the library how to serialize and deserialize.
Since you said you're new to scala, I imagine that the concept of an implicit is still somewhat foreign. I recommend you play around with it / do some reading to get a grasp of what to do when you see that the compiler is complaining about not being able to find an implicit it needs.
Bonus round
So, you might really not want to do that work yourself, and there is a way to avoid having to do it so you can do Json.format[AccountStatus]. You see Json.format uses the apply and unapply methods to do its dirty work. In scala, these two methods are defined automatically for case classes. But there's no reason you can't define them yourself and get everything they give you for free!
So, what do apply and unapply look like type signature wise? It changes per class, but in this case apply should match Int => AccountStatus (a function that goes from an int to an AccountStatus). So it's defined like so:
def apply(i: Int): AccountStatus = fromInt(i)
and unapply is similar to the reverse of this, but it needs to return an Option[Int], so it looks like
def unapply(as: AccountStatus): Option[Int] = Option(as.as)
with both of these defined you don't need to define the reads and writes yourself and instead can just call
// this is still inside the AccountStatus object { ... }
implicit val asFormat = Json.format[AccountStatus]
and it will work in a similar fashion.
.P.S. I'm traveling today, but feel free to leave any comments if some of this doesn't make sense and I'll try to get back to you later on

How to output JSON from Slick query result?

I'm a newbie when it comes to Scala and right now trying to learn Slick. I have a database set up much like in the Slick tutorial and querying it with
val users:TableQuery[user] = TableQuery[user]
How do I output users -value in to JSON from Play framework? I have tried defining case class for user and making reader+writer for userFormat = Json.format[user] but I have no clue where I'm going wrong or anything. I've done similar kind of stuff with Spray.IO and it seemed simple and straightforward there, but I want to learn how to do it with Play and Slick and I am completely lost :(
Here is my Tables.scala:
package assets
import org.joda.time.DateTime
import play.api.libs.json._
import scala.slick.driver.MySQLDriver.simple._
import com.github.tototoshi.slick.MySQLJodaSupport._
import scala.slick.lifted.{ProvenShape}
case class user
(
id:Int,
textId:String,
sessionId:String,
stamp:DateTime
)
class users(tag: Tag) extends Table[(Int, String, String, DateTime)](tag, "USER") {
implicit object userFormat extends Format[user] {
def writes(a: user): JsValue = {
Json.obj(
"id" -> JsNumber(a.id),
"textId" -> JsString(a.textId),
"sessionId" -> JsString(a.sessionId),
"stamp" -> JsString(a.stamp.toString())
)
}
def reads(json: JsValue): user = user(
(json \ "id").as[Int],
(json \ "textId").as[String],
(json \ "sessionId").as[String],
(json \ "stamp").as[DateTime]
)
}
def id: Column[Int] = column[Int]("id", O.PrimaryKey)
def textId: Column[String] = column[String]("textId")
def sessionId: Column[String] = column[String]("sessionId")
def stamp: Column[DateTime] = column[DateTime]("stamp")
def * : ProvenShape[(Int, String, String, DateTime)] = (id,textId,sessionId,stamp)
}
First of all, please add some context to your question as it's very hard to see what is not working (e.g. do you get compilation errors? Do you get exceptions?).
With respect to the question content, you should think of getting data from slick and translating it into JSON as two totally independent steps; in fact, there is nothing in slick that will help you in making JSON out of your case classes.
Given that, you will need to run your slick queries and get case class instances for your types out of them, e.g. you should get a Seq[user]. To convert that to JSON you should invoke Json.toJson(myUserSeq). There is one thing that will probably get in your way for what I can see in your code: to have the Format correctly in scope you should put it in the companion object of your case class, e.g.:
case class User(...)
object User {
implicit object format extends Format[User] { ... }
}
As a sideline, please be aware of the language's coding standards if you want people to help you and your code to be readable (e.g. having lower-case class names in your code makes it very hard to follow).

Scala toJson when using net.liftweb.mongodb.record.MongoRecord and Argonaut

I'm scala newbie and come from a Ruby background so and am having trouble rendering json response in my web service for which I use scalatra, mongodb with liftweb mongo record and argonaut for JSon serialisation and deserialisation.
However based on the examples given at http://argonaut.io/ I'm unable to figure out how this would work when using the net.liftweb.mongo.record library.
On compiling this i get a error which says a type mismatch. The error description follows the code snippet.
package firstscalatraapp
import org.scalatra
import net.liftweb.mongodb._
import net.liftweb.mongodb.record.MongoRecord
import net.liftweb.mongodb.record.field.ObjectIdPk
import net.liftweb.record.field.StringField
import net.liftweb.record.field.IntField
import net.liftweb.record.field.PasswordField
import net.liftweb.record.field.DateTimeField
import net.liftweb.mongodb.record.MongoMetaRecord
import argonaut._
import Argonaut._
case class Person private extends MongoRecord[Person] with ObjectIdPk[Person] {
def meta = Person
object age extends IntField(this, 3)
object name extends StringField(this, 29)
object created_at extends DateTimeField(this)
object password extends PasswordField(this)
}
object Person extends Person with MongoMetaRecord[Person] {
implicit def PersonCodecJson: CodecJson[Person] =
casecodec3(Person.apply, Person.unapply)("name", "age", "things")
}
The Error i get is
[error] found : () => firstscalatraapp.Person
[error] required: (?, ?, ?) => ?
[error] casecodec3(Person.apply, Person.unapply)("name", "age", "things")
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
which seems logical because the constructor does not accept any parameters and the mongo library seems to be generating the val for the fields that i need for the class (I still don't fully understand what the lift mongo wrapper does yet).
So how do i define the implicit to be able to find serialise an object of type person.
Also how do I define serialisation capabilities when i'm dealing with collections. For instance when I have a List[Person].
Thanks in advance. I would really appreciate any help i can get on this.
I'm just about to start using Argonaut so I'm no expert on that but with that said your initial problem seems obvious.
casecodec3 needs a constructor and a deconstructor for the class you're defining the codec for. In the examples of Argonaut they're using case classes and these have automatically generated companion objects with apply/unapply for the fields defined. Which for casecodec3 needs to be 3. In your case, the case class is of zero-arity - you have no case class fields at all. The fields of the record are defined as inner objects with their own apply-methods (very imperative stuff). That's just the way lifts records are defined. So your apply method is just () => Person.
casecodec3 wants a function from a 3-tuple to Person and from Person to a 3-tuple. I would suggest skipping the case definition if you're going to use lift record. And create functions on the side instead. Something like:
object Person extends Person with MongoMetaRecord[Person] {
implicit def PersonCodecJson: CodecJson[Person] =
casecodec3(parse, serialize)("name", "age", "things")
// Something like
def parse(name: String, age: Int, things: Something) = {
val p = Person.createRecord
p.name(name)
...
}
def serialize(p: Person) = (p.name.get, p.age.get, p.things.get)
}
As for your other questions I think you can head back to argonaut.io again. Their documentation seems quite alright - maybe it was worse when you posted this question as it is kind of old?
I'm going to try to replace all my serialization from lift-json to argonaut right now so if you're still stuck (probably not) I might be able to answer better in a bit.