scala / pickling

Fast, customizable, boilerplate-free pickling support for Scala
lampwww.epfl.ch/~hmiller/pickling
BSD 3-Clause "New" or "Revised" License
831 stars 79 forks source link

Weird behavior when pickling org.joda.time.DateTime #33

Closed emchristiansen closed 10 years ago

emchristiansen commented 11 years ago

When picking then unpickling a DateTime, the original value of the DateTime is lost, replaced with the current time.

Here's a console session:

import scala.pickling._
import binary._
import org.joda.time.DateTime

val date = DateTime.now
// date: org.joda.time.DateTime = 2013-09-16T13:04:39.214+02:00

// The unpickled date is different.
date.pickle.unpickle[DateTime]
// org.joda.time.DateTime = 2013-09-16T13:05:51.761+02:00

// Calling the same line again produces yet another result.
date.pickle.unpickle[DateTime]
// org.joda.time.DateTime = 2013-09-16T13:06:08.124+02:00
yashton commented 11 years ago

I am also having this issue.

scala> import scala.pickling._
scala> import json._
scala> import org.joda.time.DateTime

scala> val date = DateTime.now
date: org.joda.time.DateTime = 2013-09-23T15:59:46.201-06:00

scala> date.pickle
res1: scala.pickling.json.JSONPickle =
JSONPickle({
  "tpe": "org.joda.time.DateTime"
})

Tried it with JSON serialization, there are no populated members, just the type. The parameterless constructor and DateTime.now both yield an object with current system timestamp.

Also fails for DateTime with specified constructor. The result of the deserialization is the current time.

scala> val date = new DateTime(2013, 9, 1, 21, 50, 21)
date: org.joda.time.DateTime = 2013-09-01T21:50:21.000-06:00

scala> date.pickle
res2: scala.pickling.json.JSONPickle =
JSONPickle({
  "tpe": "org.joda.time.DateTime"
})

scala> date.pickle.unpickle[DateTime]
res3: org.joda.time.DateTime = 2013-09-23T16:04:23.494-06:00
})
yashton commented 11 years ago

I got this working by defining a custom SPickler/Unpickler implementation, using exported ISO 8601 string and corresponding constructor.

import org.joda.time._
import scala.pickling._
import scala.pickling.binary._
import org.scalatest._

class PicklingSpec extends FunSpec with matchers.ShouldMatchers {
  class CustomDateTimePickler(implicit val format: PickleFormat)
    extends SPickler[DateTime] with Unpickler[DateTime] {
    def pickle(picklee: DateTime, builder: PBuilder) {
      builder.beginEntry(picklee)
      builder.putField("iso8601", b =>
        b.hintTag(FastTypeTag.ScalaString).beginEntry(picklee.toString()).endEntry())
      builder.endEntry()
    }

    def unpickle(tag: => FastTypeTag[_], reader: PReader): DateTime = {
      reader.beginEntry()
      val date = reader.readField("iso8601").unpickle[String]
      reader.endEntry()
      new DateTime(date)
    }
  }

  implicit def customDateTimePickler(implicit format: PickleFormat) = new CustomDateTimePickler()

  describe("Playing with custom pickling") {
    it("should pickle dates correctly") {
      val date = new DateTime(2013, 9, 5, 20, 8, 13)
      date.pickle.unpickle[DateTime] should be (date)
    }
  }
}

Which returns correctly

% sbt test
....
[info] PicklingSpec:
[info] Playing with pickling
[info] - should pickle dates correctly
....
JerryPreissler commented 10 years ago

Nothing to add except for a "me too" and heartfelt thanks to yashton for providing the custom SPickler/Unpickler implementation.

janschultecom commented 10 years ago

Thanks yashton, works perfectly.

From my side another "need this". Joda is just such a widely used lib so that integration should be part of pickling imho.

glidester commented 10 years ago

Thanks yashton. Was scratching my head on this one!

heathermiller commented 10 years ago

This is fixed by #211.

While it would be nice to include these custom picklers/unpicklers in the library, it puts a dependency on jodadate time, which I'm sure most users don't want (we'd be tied to whatever version we pull in in the pickling library).

Instead, we fixed the (difficult-to-fix) root cause. Which is detecting when stuff comes from Java, and then handling Java's getters/setters. It comes down to differences between constructors in Java and Scala that caused this bug – and now we differentiate between Java and Scala and do the correct thing.