Play 2.1-RC2: Converting JsValue to Scala Value - json

I am a play beginner and try to migrate my web application from Play 2.0.4 to the new shiny Play 2.1-RC2. My code doesn't compile because of the new JSON handling.
I have read Mandubians Blog, the Play 2.1 Migration guide and the Play JSON library documentation (beta) but I am still unsure what's the best way to migrate my code.
F.ex. I have a model called File with an implicit Read-Object (Play 2.0):
object File {
implicit object FileReads extends Reads[File] {
def reads(json: JsValue) = File(
(json \ "name").as[String],
(json \ "size").as[Long]
)
}
}
I use it like this in the controller (Play 2.0):
val file = webserviceResult.json.as[models.File]
The Play 2.1 Migration guide tells me to refactor it with a JsSuccess() like this (Play 2.1?):
object File {
implicit object FileFormat extends Format[File] {
def reads(json: JsValue) = JsSuccess(File(
(json \ "name").as[String],
(json \ "size").as[Long]
))
}
}
But how can I use this implicit conversion now?
Or is it better to use the implicit val-stuff like in the Twitter-example from the Play for Scala-book? Whats the best way to convert a JsValue to it's Scala value?

Or is it better to use the implicit val-stuff like in the Twitter-example from the Play for Scala-book?
Yes, for a classic conversion, it's a good solution (simple and concise).
But there is a simpler way to achieve this conversion with the "Json Macro Inception" :
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class File(name: String, size: Long)
implicit val fileFormat = Json.format[File]
val json = Json.parse("""{"name":"myfile.avi", "size":12345}""") // Your WS result
scala> json.as[File]
res2: File = File(myfile.avi,12345)
Warning: You cannot put your formater in the companion object, it's a limitation of the current Json API.
I advice to use an object with all you json formatters, and to import it when necessary.
FYI, the raw formater should be written like this:
implicit val rawFileRead: Format[File] = (
(__ \ "name").format[String] and
(__ \ "size").format[Long]
)(File.apply _, unlift(File.unapply _)) // or (File, unlift(File.unapply))
Check these two test class, there are many interesting exemples:
JsonSpec
JsonExtensionSpec

Related

How to convert java.time.LocalDate to JSValue in Play?

I'm using Play 2.3.x. The code is writing an object into JSON.
How to convert a model, which has a field of java.time.LocalDate, to JSValue?
case class ModelA(id: Int, birthday: LocalDate)
implicit val modelAWrites: Writes[ModelA] = (
(JsPath \ "id").write[Int] and
(JsPath \ "birthday").write[LocalDate]
)(unlift(ModelA.unapply))
The compiler complains that :
No Json serializer found for type java.time.LocalDate. Try to implement an implicit Writes or Format for this type.
Thanks.
If representing the LocalDate as an ISO date string (e.g. "2016-07-09") is sufficient, then the formatter becomes quite simple:
implicit val localDateFormat = new Format[LocalDate] {
override def reads(json: JsValue): JsResult[LocalDate] =
json.validate[String].map(LocalDate.parse)
override def writes(o: LocalDate): JsValue = Json.toJson(o.toString)
}
Here's a free test to prove it:
package com.mypackage
import java.time.LocalDate
import org.scalatest.{Matchers, WordSpecLike}
import play.api.libs.json.{JsString, Json}
class MyJsonFormatsSpec extends WordSpecLike with Matchers {
import MyJsonFormats._
"MyJsonFormats" should {
"serialize and deserialize LocalDates" in {
Json.toJson(LocalDate.of(2016, 7, 9)) shouldEqual JsString("2016-07-09")
JsString("2016-07-09").as[LocalDate] shouldEqual LocalDate.of(2016, 7, 9)
}
}
}
Play is able to write most primitive data types to Json, such as Ints, Strings, and so on. It however is unable to write random types to Json, which is fair enough since otherwise the framework would have to provide a serializer for any type which seems a bit unrealistic!
So, Play is telling you it doesn't know how to serialize a type of java.time.LocalDate. You need to teach Play how to write an instance of LocalDate to Json.
See here for docs on how to do that: https://www.playframework.com/documentation/2.3.x/ScalaJsonCombinators
Instead using LocalDate form java.time.LocalDate use LocalDate from org.joda.time.LocalDate or write your custom implicit
implicit val dateFormat =
Format[LocalDate](Reads.jodaLocalDateReads(pattern), Writes.jodaLocalDateWrites(pattern))
I've had this problem too, I solved it by creating the Reads and Writes myself.
implicit val localDateReads: Reads[LocalDate] = (
(__ \ "year").read[Int] and
(__ \ "month").read[Int] and
(__ \ "day").read[Int]
) (LocalDate.of(_,_,_))
implicit val LocalDateWrites = new Writes[LocalDate] {
def writes(date: LocalDate) = Json.obj(
"day" -> date.getDayOfMonth,
"month" -> date.getMonthValue,
"year" -> date.getYear
)}
In JSON it will look like this:
"date": {
"day": 29,
"month": 8,
"year": 1993
}

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)
}

Play json parse missing field as empty array

Using play-json 2.3
How do i parse this
{i: 0}
into this
case class A(i: Int, s: Seq[Int])
i would very much like to reuse Json.format macro
at the moment it gives me "missing path s"
UPD:
i ended up writing a custom format and pimping it onto JsPath:
implicit class JsPathExtensions(path: JsPath) {
//return mzero if the path is missing
def lazyFormatNullableM[T: Monoid](f: => Format[T]): OFormat[T] = OFormat(path.lazyReadNullable(f).map(_.orZero), new OWrites[T] {
override def writes(o: T) = path.lazyWriteNullable(f).writes(o.some)
})
}
If the path is missing, I don't see any way to utilize the macro without changing the class. In which case, you can define Reads using json combinators and use the macro for Writes.
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val reads: Reads[A] = (
(__ \ "i").read[Int] and
(__ \ "s").read[Seq[Int]].orElse(Reads.pure(Nil))
)(A.apply _)
implicit val writes: Writes[A] = Json.writes[A]
The json macros only cover a very limited range of use cases, and it's inevitable that you'll run into a case where you must write your own Reads or Writes.
The easiest way to utilise the Json.format[A] macro would be to slightly alter your definition of A:
case class A(i: Int, s: Option[Seq[Int]])
The usage of an Option here is pretty clear and you can map over it and do lots of good things, but if you really want to treat s like it's an empty collection, you could enhance your case class a little more:
case class Foo(i: Int, s: Option[Seq[Int]]) {
lazy val theSeq:Seq[Int] = s.getOrElse(Seq[Int]())
}
Now if you access theSeq, there is absolutely no difference between incoming JSON that omitted s and JSON that supplied an empty array for s.

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

Rendering JSON with Play! and Scala

I have a simple question regarding rendering JSON object from a Scala class. Why do I have to implemet deserializer ( read, write ).
I have the following case class:
case class User(firstname:String, lastname:String, age:Int)
And in my controller:
val milo:User = new User("Sam","Fisher",23);
Json.toJson(milo);
I get compilation error: No Json deserializer found for type models.User. Try to implement an implicit Writes or Format for this type.
In my previous project I had to implement a reader,writer object in the class for it to work and I find it very annoying.
object UserWebsite {
implicit object UserWebsiteReads extends Format[UserWebsite] {
def reads(json: JsValue) = UserWebsite(
(json \ "email").as[String],
(json \ "url").as[String],
(json \ "imageurl").as[String])
def writes(ts: UserWebsite) = JsObject(Seq(
"email" -> JsString(ts.email),
"url" -> JsString(ts.url),
"imageurl" -> JsString(ts.imageurl)))
}
}
I really recommend to upgrade to play 2.1-RC1 because here, JSON writers/readers are very simple to be defined (more details here)
But in order to help you to avoid some errors, I will give you a hint with imports:
- use these imports only! (notice that json.Reads is not included)
import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.libs.json.Writes._
and you only have to write this code for write/read your class to/from Json (of course you will have User instead of Address:
implicit val addressWrites = Json.writes[Address]
implicit val addressReads = Json.reads[Address]
Now, they will be used automatically:
Example of write:
Ok(Json.toJson(entities.map(s => Json.toJson(s))))
Example of read(I put my example of doing POST for creating an entity by reading json from body) please notice addressReads used here
def create = Action(parse.json) { request =>
request.body.validate(addressReads).map { entity =>
Addresses.insert(entity)
Ok(RestResponses.toJson(RestResponse(OK, "Succesfully created a new entity.")))
}.recover { Result =>
BadRequest(RestResponses.toJson(RestResponse(BAD_REQUEST, "Unable to transform JSON body to entity.")))
}
}
In conclusion, they tried (and succeded) to make things very simple regarding JSON.
If you are using play 2.0.x you can do
import com.codahale.jerkson.Json._
generate(milo)
generate uses reflection to do it.
In play 2.1 you can use Json.writes to create a macro for that implicit object you had to create. No runtime reflection needed!
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val userWrites = Json.writes[User]
Json.toJson(milo)
I have been using jerkson (which basically is wrapper to jackson) in my project to convert objects to json string.
The simplest way to do that is:
import com.codehale.jerkson.Json._
...
generate(milo)
...
If you need to configure the ObjectMapper (e.g. adding custom serializer/deserializer, configuring output format, etc.), you can do it by creating object which extends com.codehale.jerkson.Json class.
package utils
import org.codehaus.jackson.map._
import org.codehaus.jackson.{Version, JsonGenerator, JsonParser}
import com.codahale.jerkson.Json
import org.codehaus.jackson.map.module.SimpleModule
import org.codehaus.jackson.map.annotate.JsonSerialize
object CustomJson extends Json {
val module = new SimpleModule("CustomSerializer", Version.unknownVersion())
// --- (SERIALIZERS) ---
// Example:
// module.addSerializer(classOf[Enumeration#Value], EnumerationSerializer)
// --- (DESERIALIZERS) ---
// Example:
// module.addDeserializer(classOf[MyEnumType], new EnumerationDeserializer[MyEnumType](MyEnumTypes))
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL)
mapper.setSerializationConfig(mapper.getSerializationConfig.without(SerializationConfig.Feature.WRITE_NULL_MAP_VALUES))
mapper.registerModule(module)
}
To use it in your codes:
import utils.CustomJson._
...
generate(milo)
...
In fact, this is very simple. Firstly import:
import play.api.libs.json._
Thanks to the fact, that User is a case class you can automatically create Json writes using Json.writes[]:
val milo:User = new User("Millad","Dagdoni",23)
implicit val userImplicitWrites = Json.writes[User]
Json.toJson(milo)
I haven't found it in the docs, but here is the link to the api: http://www.playframework.com/documentation/2.2.x/api/scala/index.html#play.api.libs.json.Json$
In your case, I'd use the JSON.format macro.
import play.api.libs.json._
implicit val userFormat = Json.format[User]
val milo = new User("Sam", "Fisher", 23)
val json = Json.toJson(milo)