scalajodatimeupicklenscala-time

uPickle ReadWriter for joda DateTime fails


For a project, i need JSON representation for a org.joda.time.DateTime. For JSON, i use uPickle. As there is no implicit ReaderWriter[DateTime], i have to write it myself. After googleing around, i came up with the following code:

import com.github.nscala_time.time.Imports.DateTime
import org.joda.time.format.ISODateTimeFormat
import upickle.default._

object DateTimeRW:
  val fmt = ISODateTimeFormat.dateTime()
  given ReadWriter[DateTime] = readwriter[ujson.Value].bimap[DateTime](
    dt => fmt.print(dt),
    dtstr => fmt.parseDateTime(dtstr.str)
  )

val dt = DateTime.now()
val jsonDateString = write(dt)
val dt2: DateTime = read(dt.toString)

It fails. I do not even understand the errors:

missing argument for parameter evidence$5 of method write in trait Api: (implicit evidence$5: upickle.default.Writer[org.joda.time.DateTime]): String

missing argument for parameter evidence$3 of method read in trait Api: (implicit evidence$3: upickle.default.Reader[T]): T

It seems that my implementation is wrong, but i cannot detect any syntactical error like a missing argument. Google finds NO answers for the error descriptions. Can someone help?


Solution

  • This compiles and works:

    import com.github.nscala_time.time.Imports.DateTime
    import org.joda.time.format.ISODateTimeFormat
    import upickle.default._
    import upickle.default.ReadWriter.join
    
    object DateTimeRW:
        val fmt = ISODateTimeFormat.dateTime()
        given ReadWriter[DateTime] = readwriter[ujson.Value].bimap[DateTime](
            x => fmt.print(x),
            dtstr => fmt.parseDateTime(dtstr.str)
        )
    import DateTimeRW.given_ReadWriter_DateTime
    
    def main(args: Array[String]): Unit = {
      val dt = DateTime.now()
      val jsonDateString = write(dt)
      println(jsonDateString)
      val dt2: DateTime = read(jsonDateString)
      println(dt2)
    }
    

    sbt:

    ThisBuild / version := "0.1.0-SNAPSHOT"
    
    ThisBuild / scalaVersion := "3.4.2"
    
    lazy val root = (project in file("."))
      .settings(
        name := "ScalaStuff"
      )
    
    libraryDependencies += "com.lihaoyi" %% "upickle" % "4.0.0-RC1"
    libraryDependencies += "joda-time" % "joda-time" % "2.12.7"
    libraryDependencies += "com.github.nscala-time" %% "nscala-time" % "2.32.0"
    
    

    This one compiles and works as well (it is based on this example):

    import com.github.nscala_time.time.Imports.DateTime
    import org.joda.time.format.ISODateTimeFormat
    import upickle.default._
    import upickle.default.ReadWriter.join
    
    //object DateTimeRW:
    //    val fmt = ISODateTimeFormat.dateTime()
    //    given ReadWriter[DateTime] = readwriter[ujson.Value].bimap[DateTime](
    //        x => fmt.print(x),
    //        dtstr => fmt.parseDateTime(dtstr.str)
    //    )
    //import DateTimeRW.given_ReadWriter_DateTime
    
    object DateTimeRw2:
        val fmt = ISODateTimeFormat.dateTime()
        implicit val rw: ReadWriter[DateTime] = upickle.default.readwriter[String].bimap[DateTime](
             x => fmt.print(x),
             dtAsStr => DateTime.parse(dtAsStr, fmt)
        )
    import DateTimeRw2.*
    
    def main(args: Array[String]): Unit = {
      val dt = DateTime.now()
      val jsonDateString = write(dt)
      println(jsonDateString)
      val dt2: DateTime = read(jsonDateString)
      println(dt2)
    }
    

    Note that I didn't have to be so explicit in my import as I've been in the other case.

    Unfortunately, I have to admit, I don't know enough about the compiler to explain you why exactly in the other case you need to be explicit in importing the given (just import DateTimeRw.* doesn't do it), and why when using the implicit val, it seems to work. I probably need to brush up my knowledge on how the compiler resolves these.

    You can assign your own name if you don't like the compiler default:

    object DateTimeRW:
        val fmt = ISODateTimeFormat.dateTime()
        given rw: ReadWriter[DateTime] = readwriter[ujson.Value].bimap[DateTime](
            x => fmt.print(x),
            dtstr => fmt.parseDateTime(dtstr.str)
        )
    import DateTimeRW.rw
    

    Note:

    read(dt.toString)

    This is incorrect. You want: read(jsonDateString).