0

I have read this question (and the other ones on SO), but I still don't manage to convert a JsValue to a case class with a Joda DateTime.

Here is the JSON I have:

val json = Json.parse(
    """
    {
        "message": "Bla bla bla",
        "updated_time": "2016-09-17T12:48:12+0000"
    }
    """
)

And the corresponding case class is:

import org.joda.time.DateTime
final case class FacebookPost(message: String, updated_time: DateTime)

I also added this implicit DateTime reader (I tried some other as well):

 implicit val readsJodaLocalDateTime = Reads[DateTime](js =>
        js.validate[String].map[DateTime](dtString => new DateTime(dtString)))

But when I try to convert my json to the corresponding case class (json.as[FacebookPost]), I get this error:

play.api.libs.json.JsResultException: `JsResultException(errors:List((/updated_time,List(ValidationError(List(error.expected.jodadate.format),WrappedArray(yyyy-MM-dd))))))`

What am I doing wrong?

Community
  • 1
  • 1
Simon
  • 6,025
  • 7
  • 46
  • 98

1 Answers1

4

Your problem is the format of your datetime: 2016-09-17T12:48:12+0000.

By default, Joda's DateTime class can't automatically figure out the format you're using, so you have to provide a hint using DateTimeFormat.

Example for our custom Reads[DateTime]:

import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._ 

val dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"

val customReads = Reads[DateTime](js =>
  js.validate[String].map[DateTime](dtString =>
    DateTime.parse(dtString, DateTimeFormat.forPattern(dateFormat))
  )
)

implicit val reads: Reads[FacebookPost] = (
  (__ \ "message").read[String] and
  (__ \ "updated_time").read[DateTime](customReads)
)(FacebookPost.apply _)

Then, it's possible to do the following:

val json = Json.parse(
  """
    {
      "message": "Bla bla bla",
      "updated_time": "2016-09-17T12:48:12+0000"
    }
   """)

val post = json.validate[FacebookPost] 
// => JsSuccess(FacebookPost(Bla bla bla,2016-09-17T14:48:12.000+02:00))

edit:

You don't need to create the Read[DateTime] from scratch, there is a predefined method in Play that helps you with that:

object CustomReads /* or wherever you want to define it */ {
  val dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
  implicit val jodaDateReads = play.api.libs.json.Reads.jodaDateReads(dateFormat)
}

Done. Just import whenever you need it, e.g. in the companion object of your model classes:

object FacebookPost {
  import utils.CustomReads._
  implicit val reads: Reads[FacebookPost] = Json.reads[FacebookPost]
}

You can "override" the implicit Read[DateTime] provided by Play by exluding it from your imports:

import play.api.libs.json.Reads.{DefaultJodaDateReads => _, _}
fxlae
  • 905
  • 8
  • 17
  • Ok it works like this, but I would like to have a solution in order not to have to write implicit Reads for every class using a DateTime. And it should be possible because I do it when I convert a JsValue to a Char for example. – Simon Nov 03 '16 at 16:59
  • Ok, so is there a way to override them? – Simon Nov 03 '16 at 16:59
  • Thanks for your edit, it seems to be the good way to do it but I still get the same error... – Simon Nov 03 '16 at 18:31
  • If it is realIy the same error as in your first post, then you're still accidentially importing Play's implicit `Reads[DateTime]`. I added a [minimal working example on github](https://github.com/fxlae/play-custom-jodadate-reads), maybe it helps. – fxlae Nov 03 '16 at 19:08
  • Perfect responses, I made it work with the example you provided. Thanks ! – Simon Nov 04 '16 at 09:28