Serializing a scala object into a JSon String using lift-json - json

I am wondering, would you please let me know how can I use lift-json to serialize a simple bean class into json string (I'm using v2.0-M1).
I tried:
val r = JsonDSL.pretty(JsonAST.render(myBean))
and I am getting
[error] found : MyBean
[error] required: net.liftweb.json.JsonAST.JValue

You can "decompose" a case class into JSON and then render it. Example:
scala> import net.liftweb.json.JsonAST._
scala> import net.liftweb.json.Extraction._
scala> import net.liftweb.json.Printer._
scala> implicit val formats = net.liftweb.json.DefaultFormats
scala> case class MyBean(name: String, age: Int)
scala> pretty(render(decompose(MyBean("joe", 35))))
res0: String =
{
"name":"joe",
"age":35
}
But sometimes it is easier to use DSL syntax:
scala> import net.liftweb.json.JsonDSL._
scala> val json = ("name" -> "joe") ~ ("age" -> 35)
scala> pretty(render(json))
res1: String =
{
"name":"joe",
"age":35
}

Related

Scala - Ignore case class field when decoding JSON

I'm playing with the example ADT in the circe documentation to reproduce an issue that I have with JSON decoding.
To achieve that, I'm using ShapesDerivation :
scala> object ShapesDerivation {
|
| implicit def encodeAdtNoDiscr[Event, Repr <: Coproduct](implicit
| gen: Generic.Aux[Event, Repr],
| encodeRepr: Encoder[Repr]
| ): Encoder[Event] = encodeRepr.contramap(gen.to)
|
| implicit def decodeAdtNoDiscr[Event, Repr <: Coproduct](implicit
| gen: Generic.Aux[Event, Repr],
| decodeRepr: Decoder[Repr]
| ): Decoder[Event] = decodeRepr.map(gen.from)
|
| }
defined object ShapesDerivation
The ADT to decode is composed by two values : a simple case class and another one that I have dedicated Encoder / Decoder (to reproduce in minimal example the issue that I really have) :
scala> :paste
// Entering paste mode (ctrl-D to finish)
sealed trait Event
object Event {
case class Foo(i: Int) extends Event
case class Bar(f : FooBar) extends Event
case class FooBar(x : Int)
implicit val encoderFooBar : Encoder[FooBar] = new Encoder[FooBar] {
override def apply(a: FooBar): Json = Json.obj(("x", Json.fromInt(a.x)))
}
implicit val decodeFooBar: Decoder[FooBar] = new Decoder[FooBar] {
override def apply(c: HCursor): Result[FooBar] =
for {
x <- c.downField("x").as[Int]
} yield FooBar(x)
}
}
Then when I try to decode a simple value like this, it's working well :
scala> import ShapesDerivation._
import ShapesDerivation._
scala> decode[Event](""" { "i" : 10 }""")
res1: Either[io.circe.Error,Event] = Right(Foo(10))
But if I tried to decode something that should be a Bar that contains a Foobar, I get a decoding failure :
scala> decode[Event](""" { "x" : 10 }""")
res2: Either[io.circe.Error,Event] = Left(DecodingFailure(CNil, List()))
But this one works because I explicitely put the case class field name :
scala> decode[Event](""" { "f" : { "x" : 10 }}""")
res7: Either[io.circe.Error,Event] = Right(Bar(FooBar(10)))
I don't what to put the case class field, directly the JSON but I think it's not possible to achieve a such behaviour. The reason why I think it's impossible is how it will know to match the good case class if there is not the field but I want to be sure that there is no way with circe to do that
Here's how you do it using just semi-auto derivation.
import io.circe.Decoder.Result
import io.circe.{Decoder, Encoder, HCursor, Json}
import io.circe.parser._
import io.circe.generic.semiauto._
object Example extends App {
sealed trait Event
object Event {
case class Foo(i: Int) extends Event
object Foo {
implicit val decoder: Decoder[Foo] = deriveDecoder
}
case class Bar(f: FooBar) extends Event
object Bar {
implicit val decoder: Decoder[Bar] = Decoder[FooBar].map(Bar.apply)
}
implicit val decoder: Decoder[Event] = Decoder[Foo].widen.or(Decoder[Bar].widen)
}
case class FooBar(x: Int)
object FooBar {
implicit val encoderFooBar: Encoder[FooBar] = deriveEncoder
implicit val decodeFooBar: Decoder[FooBar] = deriveDecoder
}
println(decode[Event](""" { "x" : 10 }"""))
}
Outputs
Right(Bar(FooBar(10)))
It gets a bit noisy with the explicit decoders, but if you care about compilation speed, it's the way to go since you'll only derive decoders once.

Play json absent-sensitive Reads

I'd like to have a reads like Reads[Patch[T]] for a case class like this
sealed trait Patch[+T]
case class Update[+T](value: T) extends Patch[T]
case object Delete extends Patch[Nothing]
case object Ignore extends Patch[Nothing]
where a missing json value reads to Ignore, a null json value reads to Delete and a valid present value reads to Patch.
Is it possible to implement a Reads like this?
Json4s has a JNothing type, does play json have some way to achieve the same functionality (I know there is no nothing type under JsValue)?
Edit: for context on how this might be used see the json merge patch rfc.
Leaving aside discussions of whether Patch[Nothing] is a good idea, if we use this family of objects:
sealed trait Patch[+T]
case class Update[+T](value: T) extends Patch[T]
case object Delete extends Patch[Nothing]
case object Ignore extends Patch[Nothing]
We can get the desired behaviour by implementing a wrapper class:
case class PatchContainer[T](patch: Patch[T])
We have to do this as otherwise we lose the all-important distinction between a null value and a completely missing patch.
Now we can write a Reads for a PatchContainer[T] as long as we supply a suitable Reads[T] (e.g. for a String or Int etc):
class PatchContainerJson[T](implicit val rdst:Reads[T]) {
implicit val patchContainerReads = new Reads[PatchContainer[T]] {
override def reads(json: JsValue): JsResult[PatchContainer[T]] = {
json.validate[JsObject].map { obj =>
(obj \ "patch").asOpt[T].fold[PatchContainer[T]] {
if (obj.keys.contains("patch")) {
PatchContainer(Delete)
} else {
PatchContainer(Ignore)
}
} { v =>
PatchContainer(Update(v))
}
}
}
}
}
The "trick" here is detecting whether there is a patch key in the object (using keys.contains), to get the desired Delete vs Ignore behaviour.
Examples of usage:
scala> import play.api.libs.json._
scala> val json = Json.parse(""" { "patch": 42 } """ )
json: play.api.libs.json.JsValue = {"patch":42}
scala> val pcti = new PatchContainerJson[Int]()
scala> import pcti._
scala> val result = json.validate[PatchContainer[Int]]
result: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Update(42)),)
scala> result.get.patch
res0: models.Patch[Int] = Update(42)
and
...
scala> val ignoredJson = Json.parse(""" { } """)
scala> ignoredJson.validate[PatchContainer[Int]]
res1: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Ignore),)
and
scala> val deleteJson = Json.parse(""" { "patch": null } """)
scala> deleteJson.validate[PatchContainer[Int]]
res2: play.api.libs.json.JsResult[models.PatchContainer[Int]] = JsSuccess(PatchContainer(Delete),)

how to return custom json in scalatra

Scalatra Code:
import org.scalatra._
import org.json4s.{DefaultFormats, Formats}
import org.scalatra.json._
class AppServlet extends AppStack with JacksonJsonSupport{
protected implicit lazy val jsonFormats: Formats = DefaultFormats
private def generateJSON():((String, String),(String, String)) = {
val json = ("Firstname" -> "joe", "LastName" -> "cole")
json
}
before() {
contentType = formats("json")
}
get("/") {
generateJSON
}
}
I am trying to return simple json using scalatra framework and the output is something like this {"_1":{"Firstname":"joe"},"_2":{"LastName":"cole"}}. I dont need _1 or _2 to be printed. Please note i am not trying to return any objects. I just need to make my own json and then return it. It is not associated with any data model. Any help is appreciated.
What you create is a tuple of (String, String), that's not surprising the output is like that. You should either create a case class, or, since you use json4s, return:
// don't forget this:
// import org.json4s.JsonDSL._
("Firstname" -> "joe") ~ ("LastName" -> "cole")
import org.scalatra._
import org.json4s.{DefaultFormats, Formats}
import org.scalatra.json._
import org.json4s._
import org.json4s.JsonDSL._
class AppServlet extends AppStack with JacksonJsonSupport{
protected implicit lazy val jsonFormats: Formats = DefaultFormats
private def generateJSON():JObject = {
val json = ("Firstname" -> "joe") ~ ("LastName" -> "cole")
json
}
get("/") {
generateJSON
}
}

Akka HTTP Routing and Marshaling

I'm just starting out with Akka HTTP and I'm having a bit of trouble with the routing DSL and marshaling. The tilde in the 'route' setup results in the error:
value ~ is not a member of akka.http.scaladsl.server.RequestContext ⇒
scala.concurrent.Future[akka.http.scaladsl.server.RouteResult]
possible cause: maybe a semicolon is missing before 'value ~'?
Also, the JSON marshaling in the 'get' clause causing the error:
◾Cannot find JsonWriter or JsonFormat type class for
scala.collection.immutable.Map[String,scala.collection.mutable.Map[String,Tweet]]
◾not enough arguments for method toJson: (implicit writer:
spray.json.JsonWriter[scala.collection.immutable.Map[String,scala.collection> .mutable.Map[String,Tweet]]])spray.json.JsValue.
Unspecified value parameter writer.
I've followed the documentation examples fairly closely, so I'd appreciate help in understanding these errors and how to resolve them. Thanks.
API
import akka.actor.ActorSystem
import scala.concurrent.Future
import akka.stream.ActorMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives.path
import akka.http.scaladsl.server.Directives.pathPrefix
import akka.http.scaladsl.server.Directives.post
import akka.http.scaladsl.server.Directives.get
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives.{entity, as}
import akka.http.scaladsl.model.StatusCodes.{Created, OK}
import spray.json._
import DefaultJsonProtocol._
import akka.stream.Materializer
import scala.concurrent.ExecutionContext
trait RestApi {
import TweetProtocol._
import TweetDb._
implicit val system: ActorSystem
implicit val materializer: Materializer
implicit val execCtx: ExecutionContext
val route =
pathPrefix("tweets") {
(post & entity(as[Tweet])) { tweet =>
complete {
Created -> Map("id" -> TweetDb.save(tweet)).toJson
}
} ~
(get) {
complete {
OK -> Map("tweets" -> TweetDb.find()).toJson
}
}
}
}
object TweetApi extends App with RestApi {
implicit val system = ActorSystem("webapi")
implicit val materializer = ActorMaterializer()
implicit val execCtx = system.dispatcher
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
Console.readLine()
bindingFuture.flatMap(_.unbind()).onComplete { _ => system.shutdown() }
}
Protocol
import spray.json.DefaultJsonProtocol
case class Tweet(author: String, body: String)
object TweetProtocol extends DefaultJsonProtocol {
implicit val TweetFormat = jsonFormat2(Tweet.apply)
}
Pseudo-database
import scala.collection.mutable.Map
object TweetDb {
private var tweets = Map[String, Tweet]()
def save(tweet: Tweet) = {
val id: String = java.util.UUID.randomUUID().toString()
tweets += (id -> tweet)
id
}
def find() = tweets
def findById(id: String) = tweets.get(id)
}
For your 1st error, try the suggestion from the comment, ie. import all from Directives
For second part
◾Cannot find JsonWriter or JsonFormat type class for scala.collection.immutable.Map[String,scala.collection.mutable.Map[String,Tweet]]
◾not enough arguments for method toJson: (implicit writer: spray.json.JsonWriter[scala.collection.immutable.Map[String,scala.collection> .mutable.Map[String,Tweet]]])spray.json.JsValue. Unspecified value parameter writer.
You need to define JsonFormat for Map[String, mutable.Map[String, Tweet]]
By creating an object in your TweetProtocol, extending RootJsonFormat
eg.
type DBEntry = Map[String, mutable.Map[String, Tweet]]
object TweetProtocol extends DefaultJsonProtocol {
implicit object DBEntryJsonFormat extends RootJsonFormat[DBEntry] {
override def read(json: JSValue) {
// your implementation
}
override def write(dbEntry: DBEntry) {
// implementation
}
}
}

Play 2.1 Json serialization for traits?

I have this:
package models
import play.api.libs.json._
import play.api.libs.functional.syntax._
object ModelWrites {
implicit val tmoWrites= Json.writes[TestModelObject]
implicit val ihWrites = Json.writes[IntHolder]
}
case class TestModelObject(s1:String, s2:String)
case class IntHolder(i1:Int, i2:Int)
trait HasInts {
val ints: List[IntHolder]
}
When I do this:
scala> val tmo = new TestModelObject("hello", "world") with HasInts {
val ints = List(IntHolder(1,2), IntHolder(3,4))
}
scala> Json.toJson(tmo)
res0: play.api.libs.json.JsValue = {"s1":"hello","s2":"world"}
how can I implicity serialize the mixed-in val 'ints'? Like:
scala> val someInts = List(IntHolder(8,9), IntHolder(10,11))
someInts: List[models.IntHolder] = List(IntHolder(8,9), IntHolder(10,11))
scala> Json.toJson(someInts)
res1: play.api.libs.json.JsValue = [{"i1":8,"i2":9},{"i1":10,"i2":11}]
Note: if I try: implicit val hasIntsWrites = Json.writes[HasInts] I (expectedly?) get:
[error] Models.scala:10: No unapply function found
[error] implicit val hasIntsWrites = Json.writes[HasInts]
[error] ^
You're not going to be able to use the experimental "Inception" feature (Json.writes[...]) directly here, since that only works on case classes. You can, however, build on the Writes instances that Inception can provide to accomplish what you want with only a very little boilerplate.
Note that I'm ignoring the question of whether mixing in a trait when instantiating a case class like this is a good idea—it probably isn't—but the approach I give here will work in the more general case as well.
First for the classes and imports (no changes here):
case class TestModelObject(s1: String, s2: String)
case class IntHolder(i1: Int, i2: Int)
trait HasInts { val ints: List[IntHolder] }
import play.api.libs.json._
import play.api.libs.functional.syntax._
Now we need to put all our lower-priority instances into a trait to make sure that the compiler will pick the right one, since TestModelObject with HasInts is a subtype of both TestModelObject and HasInts:
trait LowPriorityWritesInstances {
implicit val tmoWrites = Json.writes[TestModelObject]
implicit val ihWrites = Json.writes[IntHolder]
implicit object hiWrites extends OWrites[HasInts] {
def writes(hi: HasInts) = Json.obj("ints" -> hi.ints)
}
}
And now the main event:
object WritesInstances extends LowPriorityWritesInstances {
implicit val tmowhiWrites = new Writes[TestModelObject with HasInts] {
def writes(o: TestModelObject with HasInts) =
tmoWrites.writes(o) ++ implicitly[OWrites[HasInts]].writes(o)
}
}
And we're done:
scala> import WritesInstances._
import WritesInstances._
scala> val tmo = new TestModelObject("hello", "world") with HasInts {
| val ints = List(IntHolder(1, 2), IntHolder(3, 4))
| }
scala> println(Json.toJson(tmo))
{"s1":"hello","s2":"world","ints":[{"i1":1,"i2":2},{"i1":3,"i2":4}]}
As desired.