13

I'm developing a Play application, and I'm trying to use a Joda DateTime object into my case class.

package model

import org.joda.time.DateTime
import play.api.libs.json._

case class User(name: String, created: DateTime)

object User {
  implicit val yourJodaDateReads = Reads.jodaDateReads("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
  implicit val yourJodaDateWrites = Writes.jodaDateWrites("yyyy-MM-dd'T'HH:mm:ss.SSSZ'")
  implicit val userFormat = Json.format[User]

  def main(args: Array[String]) {

  val value = Json.parse("{ \"name\" : \"hello\" , \"created\" : \"2015-07-16T20:32:04.046+02:00\" }")

  println(Json.toJson(new User("user", new DateTime())))
  println(Json.fromJson(value))
 }
}

Based on this solution, I'm getting this error:

Error:(18, -1) Play 2 Compiler: 
 /activator-1.3.2/notifier-app/app/model/Test.scala:18: ambiguous implicit    values:
 both value yourJodaDateReads in object User of type => play.api.libs.json.Reads[org.joda.time.DateTime]
    and value userFormat in object User of type => play.api.libs.json.OFormat[model.User]

I'm using Activator 1.3.2 and Play 2.3.8.

Could you please advice me ?

Thanks in advance.

update

I understand there is a conflict with the implicit value in play.api.libs.json.Reads

implicit val DefaultJodaDateReads = jodaDateReads("yyyy-MM-dd") 

How can I resolve this issue ?

Community
  • 1
  • 1
Guillaume
  • 694
  • 1
  • 6
  • 15
  • possible duplicate of [Custom JodaTime serializer using Play Framework's JSON library?](http://stackoverflow.com/questions/18255504/custom-jodatime-serializer-using-play-frameworks-json-library) – Mario Camou Jul 17 '15 at 09:30
  • Please read my post instead of replying a bad response. I've quoted this link because my code is based on this thread.. – Guillaume Jul 18 '15 at 18:51

5 Answers5

24

Expecting a better alternative, here my workaround:

val dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

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

val jodaDateWrites: Writes[DateTime] = new Writes[DateTime] {
  def writes(d: DateTime): JsValue = JsString(d.toString())
}

val userReads: Reads[User] = (
  (JsPath \ "name").read[String] and
    (JsPath \ "created").read[DateTime](jodaDateReads)
  )(User.apply _)

val userWrites: Writes[User] = (
  (JsPath \ "name").write[String] and
   (JsPath \ "created").write[DateTime](jodaDateWrites)
  )(unlift(User.unapply))

implicit val userFormat: Format[User] = Format(userReads, userWrites)
Guillaume
  • 694
  • 1
  • 6
  • 15
24

In play 2.6, the canonical way to serialize/deserialize joda DateTime json is by using the play-json-joda library. Import the library by updating your build.sbt. Then create json reader and json writers like this :

import play.api.libs.json.JodaWrites
implicit val dateTimeWriter: Writes[DateTime] = JodaWrites.jodaDateWrites("dd/MM/yyyy HH:mm:ss")
import play.api.libs.json.JodaReads
implicit val dateTimeJsReader = JodaReads.jodaDateReads("yyyyMMddHHmmss")
Moebius
  • 6,242
  • 7
  • 42
  • 54
  • 3
    Play 2.6 has so many nice features, i keep finding little quality of life changes like this – ObjectiveTruth Aug 08 '17 at 17:05
  • 1
    Make sure the [`DateTimeFormat`](https://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html) pattern argument matches the `String` you're reading; e.g. in my case (Postgres > Django > Play), the pattern `"yyyy-MM-dd'T'HH:mm:ss.SSSZ"` failed, but `"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"` worked . – Ricardo Mar 11 '22 at 23:06
6

Try these:

implicit val dateWrites = jodaDateWrites("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
implicit val dateReads = jodaDateReads("yyyy-MM-dd'T'HH:mm:ss.SSSZ")

https://github.com/playframework/playframework/blob/master/framework/src/play-json/src/main/scala/play/api/libs/json/Writes.scala#L411

https://github.com/playframework/playframework/blob/master/framework/src/play-json/src/main/scala/play/api/libs/json/Reads.scala#L645

Vincil Bishop
  • 1,594
  • 17
  • 21
6

I know this question has been answered for a while, but I found a more concise answer

val pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
implicit val dateFormat = Format[DateTime](Reads.jodaDateReads(pattern), Writes.jodaDateWrites(pattern))
implicit val userFormat = Json.format[User]
jakehschwartz
  • 1,005
  • 2
  • 13
  • 32
1

I think you should set the User type in Json.toJson and Json.fromJson functions. Instead of

println(Json.toJson(new User("user", new DateTime())))
println(Json.fromJson(value))

try:

println(Json.toJson[User](new User("user", new DateTime())))
println(Json.fromJson[User](value))

When you set the type explicitly framework will know what reads/writes to use.

Update: It is not necessarily to set type for Json.toJson function because you pass User object as function argument and framework determines the type in runtime. But for Json.fromJson[User] you must set the type, otherwise framework doesn't know type of the object you want to read.

Evghenii Todorov
  • 637
  • 6
  • 15