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

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
}

Related

How to return 2 decimal points for BigDecimal in JSON response from Scala/Play Framework

Here is my case class.
case class DecimalWrapper(money: BigDecimal, type: String)
object DecimalWrapper {
implicit val decimalWrapperMarshaller = Json.format[DecimalWrapper]
}
When trying to do Json.toJson(DecimalWrapper(2, "whole sale")) it returns
{
"money": 2
"type": "whole sale"
}
what I would like it to return is 2.00 for the money field.
I couldn't find out a way to specify a custom serialiser to always return 2 decimal points with rounding HALF_UP.
Anyone please kindly help?
Regards
Tin
Might seem more involved but it'd be a good idea to define a separate writes for BigDecimal if you intend to reuse that writes logic throughout your application. One thing to note is you have to use it explicitly, you can't declare it implicit as there is already an implicit writes defined for BigDecimal.
val decimalWrites = new Writes[BigDecimal]{
def writes(o: BigDecimal): JsValue = JsString(o.setScale(2, RoundingMode.HALF_UP).toString())
}
implicit val decimalWrapperWrites: Writes[DecimalWrapper] = (
(JsPath \ "money").write[BigDecimal](decimalWrites) and
(JsPath \ "type").write[String]
)(unlift(DecimalWrapper.unapply))
You can write your Writes manually.
Something like:
implicit val myWrites= new Writes[DecimalWrapper] {
def writes(a: DecimalWrapper): JsValue = Json.obj(
"money" -> a.money.setScale(2, RoundingMode.HALF_UP).toString(),
"type" -> a.`type`
)}

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

Custom JodaTime serializer using Play Framework's JSON library?

How do I implement a custom JodaTime's DateTime serializer/deserializer for JSON? I'm inclined to use the Play Framework's JSON library (2.1.1). There is a default DateTime serializer, but it uses dt.getMillis instead of .toString which would return an ISO compliant String.
Writing Reads[T] amd Writes[T] for case classes seems fairly straightforward, but I can't figure out how to do the same for DateTime.
There is a default DateTime serializer, but it uses dt.getMillis instead of .toString which would return an ISO compliant String.
If you look at the source, Reads.jodaDateReads already handles both numbers and strings using DateTimeFormatter.forPattern. If you want to handle ISO8601 string, just replace it with ISODateTimeFormat:
implicit val jodaISODateReads: Reads[org.joda.time.DateTime] = new Reads[org.joda.time.DateTime] {
import org.joda.time.DateTime
val df = org.joda.time.format.ISODateTimeFormat.dateTime()
def reads(json: JsValue): JsResult[DateTime] = json match {
case JsNumber(d) => JsSuccess(new DateTime(d.toLong))
case JsString(s) => parseDate(s) match {
case Some(d) => JsSuccess(d)
case None => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.date.isoformat", "ISO8601"))))
}
case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.date"))))
}
private def parseDate(input: String): Option[DateTime] =
scala.util.control.Exception.allCatch[DateTime] opt (DateTime.parse(input, df))
}
(simplify as desired, e.g. remove number handling)
implicit val jodaDateWrites: Writes[org.joda.time.DateTime] = new Writes[org.joda.time.DateTime] {
def writes(d: org.joda.time.DateTime): JsValue = JsString(d.toString())
}
I use Play 2.3.7 and define in companion object implicit reads/writes with string pattern:
case class User(username:String, birthday:org.joda.time.DateTime)
object User {
implicit val yourJodaDateReads = Reads.jodaDateReads("yyyy-MM-dd'T'HH:mm:ss'Z'")
implicit val yourJodaDateWrites = Writes.jodaDateWrites("yyyy-MM-dd'T'HH:mm:ss'Z'")
implicit val userFormat = Json.format[User]
}
Another, perhaps simpler, solution would be to do a map, for example:
case class GoogleDoc(id: String, etag: String, created: LocalDateTime)
object GoogleDoc {
import org.joda.time.LocalDateTime
import org.joda.time.format.ISODateTimeFormat
implicit val googleDocReads: Reads[GoogleDoc] = (
(__ \ "id").read[String] ~
(__ \ "etag").read[String] ~
(__ \ "createdDate").read[String].map[LocalDateTime](x => LocalDateTime.parse(x, ISODateTimeFormat.basicdDateTime()))
)(GoogleDoc)
}
UPDATE
If you had a recurring need for this conversion, then you could create your own implicit conversion, it is only a couple of lines of code:
import org.joda.time.LocalDateTime
import org.joda.time.format.ISODateTimeFormat
implicit val readsJodaLocalDateTime = Reads[LocalDateTime](js =>
js.validate[String].map[LocalDateTime](dtString =>
LocalDateTime.parse(dtString, ISODateTimeFormat.basicDateTime())
)
)

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

Play 2.1-RC2: Converting JsValue to Scala Value

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