Reputation: 6462
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?
Upvotes: 0
Views: 1474
Reputation: 905
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 => _, _}
Upvotes: 4