JSON creation in Scala - json

I am building HTTP service with scala-play. I want to serve the JSON response.
To serve JSON response, I created case classes and created instance of Writes[T] class which is similar to following
case class Point(X: Double, Y: Double)
implicit val pointWrites = new Writes[Point] {
def writes(point: Point) = Json.obj(
"X" -> point.X,
"Y" -> point.Y
)
}
I can server response like: val json = Json.toJson(Point(10, 20))
I followed above approach from play documentation.
Why do I need to define class and create Writes[T] instance for serve JSON response.
If I have to create JSON for dozens of classes, do I have to define all classes and create instances for Writes[T]?
I can easily convert any class data to JSON with following method:
def getJSON(data: Map[String, String]): JsObject = {
JsObject(data.map { el => el._1 -> JsString(el._2) })
}
All I need do is create Map[String, String] for any class to serve JSON.
Why play documentation recommends to define case class and create instance of Writes[T] to create JSON representation?
Is there any other way to serve create JSON in scala? I want to keep response time minimal so that JSON creation must be lightening fast.

You don't need to create the Writes instance. Play already have a default formatter for all the regular types (Integer/String/Boolean..)
You just need to write the case class and add an implicit formatter in the companion object.
See json.scala format[A] description from play:
/**
* Creates a Format[T] by resolving case class fields & required implicits at COMPILE-time
*
* If any missing implicit is discovered, compiler will break with corresponding error.
* {{{
* import play.api.libs.json.Json
*
* case class User(name: String, age: Int)
*
* implicit val userWrites = Json.format[User]
* // macro-compiler replaces Json.format[User] by injecting into compile chain
* // the exact code you would write yourself. This is strictly equivalent to:
* implicit val userWrites = (
* (__ \ 'name).format[String] and
* (__ \ 'age).format[Int]
* )(User.apply, unlift(User.unapply))
* }}}
*/
def format[A] = macro JsMacroImpl.formatImpl[A]
Back to your example, you only need:
case class Point(X: Double, Y: Double)
object Point{
implicit val fmtJson = Json.format[Point]
}
to get the json text just do:
val json = Point.fmtJson.writes(Point(10, 20)).toString()
will result: json = {"X":10,"Y":20}

Related

Circe Decoder - Fallback to another decoder if fails

I am using Circe for json operations. I have added custom encoders and decoders to handle some of the types, like Joda Time.
While parsing DateTime, I want to allow multiple formats to be passed.
For eg. dd-MM-yyyy'T'HH:mm:ss'Z' and dd-MM-yyyy'T'HH:mm:ss.SSS'Z'
I have defined my decoder like below:
val dateTimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
val dateTimeFormatWithMillis = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
implicit val jodaDateTimeFormat: Encoder[DateTime] with Decoder[DateTime] = new Encoder[DateTime] with Decoder[DateTime] {
override def apply(a: DateTime): Json = Encoder.encodeString(a.toString("yyyy-MM-dd'T'HH:mm:ss'Z'"))
override def apply(c: HCursor): Result[DateTime] = Decoder.decodeString.map { x =>
DateTime.parse(x, dateTimeFormat)
}.apply(c)
}
Now if i input a datetime string matching the dateTimeFormat, then the decoding will work, but if I pass the datetime in dateTimeFormatWithMillis, it will fail to process.
I know that I can use the DateTimeFormatterBuilder to add multiple parsers and process it, however, I was wondering if there is a way in Circe to chain multiple decoders to try one after another until it succeeds or reached end of chain?
You can use Decoder#or to combine decoders so that the second one is tried in case the first one fails.
Here is a working example:
import org.joda.time.DateTime
import org.joda.time.format.{DateTimeFormat, DateTimeFormatter}
import io.circe.{Decoder, Encoder}
import io.circe.parser.decode
import scala.util.Try
val dateTimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
val dateTimeFormatWithMillis = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
/** Creates a decoder that decodes a [[DateTime]] using the provided format. */
def dateTimeFormatDecoder(format: DateTimeFormatter): Decoder[DateTime] =
Decoder[String].emapTry(str => Try(DateTime.parse(str, format)))
/** [[Decoder]] for the first format (without milliseconds). */
val dateTimeWithoutMillisDecoder: Decoder[DateTime] =
dateTimeFormatDecoder(dateTimeFormat)
/** [[Decoder]] for the second format (with milliseconds). */
val dateTimeWithMillisDecoder: Decoder[DateTime] =
dateTimeFormatDecoder(dateTimeFormatWithMillis)
/** Encodes a [[DateTime]] using `Encoder[String].contramap(...)`, which is
* perhaps a slightly more idiomatic version of
* `Encoder.encodeString(a.toString("yyyy-MM-dd'T'HH:mm:ss'Z'"))` */
implicit val jodaDateTimeEncoder: Encoder[DateTime] =
Encoder[String].contramap(_.toString("yyyy-MM-dd'T'HH:mm:ss'Z'"))
implicit val jodaDateTimeDecoder: Decoder[DateTime] =
dateTimeWithoutMillisDecoder or dateTimeWithMillisDecoder
println(decode[DateTime](""" "2001-02-03T04:05:06Z" """))
println(decode[DateTime](""" "2001-02-03T04:05:06.789Z" """))
Note that the Encoder and Decoder have been separated since Decoder#or returns a Decoder, which wouldn’t work with a combined class (i.e. Encoder[DateTime] with Decoder[DateTime]).
Also, the DateTime.parse calls have been wrapped with Decoder#emapTry because the or combinator (and in general all Decoder combinators) expects to be dealing with Either values, not exceptions.

Converting JSON in one format to another in Scala

I'm looking for suggestions or libraries that can help me convert JSON (with nested structure) from one format to another in Scala.
I saw there are a few JavaScript and Java based solutions. Anything in Scala ?
I really like the Play JSON library. It's API is very clean and it's very fast even if some parts have a slightly steeper learning curve. You can also use the Play JSON library even if you aren't using the rest of Play.
https://playframework.com/documentation/2.3.x/ScalaJson
To convert JSON to scala objects (and vice versa), Play uses implicits. There is a Reads type which specifies how to convert JSON to a scala type, and a Writes type which specifies how to convert a scala object to JSON.
For example:
case class Foo(a: Int, b: String)
There are a few different routes you can take to convert Foo to JSON. If your object is simple (like Foo), Play JSON can create a conversion function for you:
implicit val fooReads = Json.reads[Foo]
or you can create a custom conversion function if you want more control or if your type is more complex. The below examples uses the name id for the property a in Foo:
implicit val fooReads = (
(__ \ "id").read[Int] ~
(__ \ "name").read[String]
)(Foo)
The Writes type has similar capabilities:
implicit val fooWrites = Json.writes[Foo]
or
implicit val fooWrites = (
(JsPath \ "id").write[Int] and
(JsPath \ "name").write[String]
)(unlift(Foo.unapply))
You can read more about Reads/Writes (and all the imports you will need) here: https://playframework.com/documentation/2.3.x/ScalaJsonCombinators
You can also transform your JSON without mapping JSON to/from scala types. This is fast and often requires less boilerplate. A simple example:
import play.api.libs.json._
// Only take a single branch from the input json
// This transformer takes the entire JSON subtree pointed to by
// key bar (no matter what it is)
val pickFoo = (__ \ 'foo).json.pickBranch
// Parse JSON from a string and apply the transformer
val input = """{"foo": {"id": 10, "name": "x"}, "foobar": 100}"""
val baz: JsValue = Json.parse(input)
val foo: JsValue = baz.transform(pickFoo)
You can read more about transforming JSON directly here: https://playframework.com/documentation/2.3.x/ScalaJsonTransformers
You can use Json4s Jackson. With PlayJson, you have to write Implicit conversions for all the case classes. If the no. of classes are small, and will not have frequent changes while development, PlayJson seems to be okay. But, if the case classes are more, I recommend using json4s.
You need to add implicit conversion for different types, so that json4s will understand while converting to json.
You can add the below dependency to your project to get json4s-jackson
"org.json4s" %% "json4s-jackson" % "3.2.11"
A sample code is given below (with both serialization and deserialization):
import java.util.Date
import java.text.SimpleDateFormat
import org.json4s.DefaultFormats
import org.json4s.jackson.JsonMethods._
import org.json4s.jackson.{Serialization}
/**
* Created by krishna on 19/5/15.
*/
case class Parent(id:Long, name:String, children:List[Child])
case class Child(id:Long, name:String, simple: Simple)
case class Simple(id:Long, name:String, date:Date)
object MainClass extends App {
implicit val formats = (new DefaultFormats {
override def dateFormatter = new SimpleDateFormat("yyyy-MM-dd")
}.preservingEmptyValues)
val d = new Date()
val simple = Simple(1L, "Simple", d)
val child1 = Child(1L, "Child1", simple)
val child2 = Child(2L, "Child2", simple)
val parent = Parent(1L, "Parent", List(child1, child2))
//Conversion from Case Class to Json
val json = Serialization.write(parent)
println(json)
//Conversion from Json to Case Class
val parentFromJson = parse(json).extract[Parent]
println(parentFromJson)
}

Create a generic Json serialization function

Is it possible to create a generic function in Scala, using Play Framework 2.2, that will serialize an arbitrary object to JSON, without having to be supplied a writer or formatter?
For instance, this non-generic code will create a JSON response given a Customer:
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class Customer(id: Int, name: String)
object scratch {
val p = Customer(1, "n")
//> p : Customer = Customer(1,n)
def createJsonResponseCustomer(data: Customer) = {
implicit val formatter = Json.format[Customer]
Json.obj("success" -> true, "data" -> Json.toJson[Customer](data))
}
createJsonResponseCustomer(p)
//> res0: play.api.libs.json.JsObject = {"success":true,"data":{"id":1,"name":"n"}}
}
To avoid having to define the formatter for each different object, I'd like to create a generic function like this:
def createJsonResponse[T](data: T) = {
implicit val formatter = Json.format[T]
Json.obj("success" -> true, "data" -> Json.toJson[T](data))
}
But this attempt produces the error No unapply function found at Json.format[T].
In other words, this works:
def getFormatter(c: Customer) = Json.format[Customer]
but this doesn't:
def getFormatterGeneric[T](c: T) = Json.format[T]
Is there any way around this?
You need to define the formatter somewhere, for each type you wish to read or write. This is because the formatter instances are resolved at compile time, not at runtime. This is a good thing, because it means trying to serialize a type that does not have a serializer becomes a compile-time error, not a runtime one.
Instead of defining the formatters on the fly, define them in a module that you can reuse, e.g.
object JsonFormatters {
implicit val customerWrites: Format[Customer] = Json.format[Customer]
}
Then import JsonFormatters._ in the scope that you want to write some JSON.
Now, you can write a generic method similar to what you wanted: you just have to specify the requirement for a formatter in the signature of your method. In practice, this is an implicit paramter of type Writes[T].
def createJsonResponse[T](data: T)(implicit writes: Writes[T]) =
Json.obj("success" -> true, "data" -> Json.toJson[T](data))
You can also write this method signature using context bound syntax, i.e.
def createJsonResponse[T : Writes](data: T) = ...
This requires that there is an instance of Writes[T] in scope; but the compiler will choose the correct instance for you based on the type T, rather than you resolving it explicitly.
Note that Writes[T] is a supertype of Format[T]; since you are only writing JSON in this method, there's no need to specify a requirement for Format[T], which would also give you Reads[T].

Json Writes in Play 2.1.1

I started using the Playframework recently and am implementing a site using Play 2.1.1 and Slick 1.0.0. I'm now trying to wrap my head around Json Writes as I want to return Json in one of my controllers.
I've been looking at several references on the subject (like this one and this one but can't figure out what I'm doing wrong.
I have a model looking like this:
case class AreaZipcode( id: Int,
zipcode: String,
area: String,
city: String
)
object AreaZipcodes extends Table[AreaZipcode]("wijk_postcode") {
implicit val areaZipcodeFormat = Json.format[AreaZipcode]
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def zipcode = column[String]("postcode", O.NotNull)
def area = column[String]("wijk", O.NotNull)
def city = column[String]("plaats", O.NotNull)
def autoInc = * returning id
def * = id ~ zipcode ~ area ~ city <> (AreaZipcode.apply _, AreaZipcode.unapply _)
}
You can see the implicit val which I'm trying to use, but when I try to return the Json in my controller by doing this:
Ok(Json.toJson(areas.map(a => Json.toJson(a))))
I'm somehow still confronted with this errormessage:
No Json deserializer found for type models.AreaZipcode. Try to implement an implicit Writes or Format for this type.
I've tried several other ways to implement the Writes. For instance, I've tried the following instead of the implicit val from above:
implicit object areaZipcodeFormat extends Format[AreaZipcode] {
def writes(a: AreaZipcode): JsValue = {
Json.obj(
"id" -> JsObject(a.id),
"zipcode" -> JsString(a.zipcode),
"area" -> JsString(a.area),
"city" -> JsString(a.city)
)
}
def reads(json: JsValue): AreaZipcode = AreaZipcode(
(json \ "id").as[Int],
(json \ "zipcode").as[String],
(json \ "area").as[String],
(json \ "city").as[String]
)
}
Can someone please point me in the right direction?
JSON Inception to the rescue! You only need to write
import play.api.libs.json._
implicit val areaZipcodeFormat = Json.format[AreaZipcode]
That's it. No need to write your own Reads and Writes anymore, thanks to the magic of Scala 2.10 macros. (I recommend that you read Play's documentation on Working with JSON, it explains a lot.)
Edit:
I didn't notice you already had the Json.format inside the AreaZipcodes object. You either need to move that line out of AreaZipcodes or import it into your current context, i.e.
import AreaZipcodes.areaZipcodeFormat

Play2 does not find my implicit Reads or Format for JSON

This is my Search Object:
package models.helper
import play.api.libs.json.Format
import play.api.libs.json.JsValue
import play.api.libs.json.JsObject
import play.api.libs.json.JsString
case class Search (name: String, `type`:String){
implicit object SearchFormat extends Format[Search] {
def reads(json: JsValue): Search = Search(
(json \ "name").as[String],
(json \ "type").as[String]
)
def writes(s: Search): JsValue = JsObject(Seq(
"name" -> JsString(s.name),
"type" -> JsString(s.`type`)
))
}
}
I'm trying ot use this class when calling a webservice using WS:
val search = response.json.as[Search]
But the scala compiler keeps complaining on this line:
No Json deserializer found for type models.helper.Search. Try to
implement an implicit Reads or Format for this type.
Could anybody tell me what I'm doing wrong?
got the example from https://sites.google.com/site/play20zh/scala-developers/working-with-json
this thread discusses the same issue but gives no solution, what example on what site? https://groups.google.com/forum/?fromgroups#!topic/play-framework/WTZrmQi5XxY
Indeed the example is wrong. You need your implicit Format[Search] value to be available in the implicit scope.
In your case the Format[Search] is defined as a nested value of the class Search, so you can reach it only from an instance of Search.
So, what you want to do is to define it in another place, where it could be referenced without having to create an instance of Search, e.g. in a Formats object:
object Formats {
implicit SearchFormat extends Format[Search] {
…
}
}
Then you can use it as follows:
import Formats.SearchFormat
val search = response.json.as[Search]
You can also get rid of the import tax by defining the Format[Search] value in the companion object of the Search class. Indeed the Scala compiler automatically looks in companion objects of type parameters when it needs an implicit value of a given type:
case class Search(name: String, `type`: String)
object Search {
implicit object SearchFormat extends Format[Search] {
…
}
}
Then you can use it without having to import it:
val search = response.json.as[Search]