muster icon indicating copy to clipboard operation
muster copied to clipboard

Json4sCodec deserializer expects an object to deserialize joda DateTime from

Open divijan opened this issue 10 years ago • 7 comments

Standalone DateTime value is serialized to string using a custom producer and deserialized back using a custom consumer just fine. However, when DateTime value is part of a case class, it is serialized to string, but fails to deserialize (causing the whole case class deserialization to fail) with a message:

MappingException: : Expected an object field for timestamp but found a JString (json4s_input.scala:44)

where timestamp is the name of the DateTime field.

divijan avatar Sep 03 '14 12:09 divijan

do your formats include the joda time formats? This error suggests they don't

casualjim avatar Sep 03 '14 16:09 casualjim

what's the source of DateTime? Is that the joda time one?
Can you share a snippet to reproduce this behavior?

casualjim avatar Sep 09 '14 06:09 casualjim

I'm sorry, notification for the first message wound up in spam. Yes, it is org.joda.time.DateTime. Here is the code ("StackEvt" should "serialize to string and deserialize back" fails):

import muster.ast.{AstNode, TextNode}
import muster.input.Consumer
import muster.output.{Producer, OutputFormatter}
import org.joda.time.{DateTimeZone, DateTime}
import com.typesafe.scalalogging.LazyLogging
import org.specs2.mutable._
import org.json4s._
import muster.codec.json4s._

class LowLevelTest extends Specification with LazyLogging {
  case class StackEvt (timestamp: DateTime)

  implicit object DateTimeConsumer extends Consumer[DateTime] {
    def consume(node: AstNode[_]): DateTime = node match {
      case TextNode(value) =>
        val timeZone = DateTimeZone.getDefault
        DateTime.parse(value).withZone(timeZone) //otherwise the parsed DateTime won't equal the original
      case n => throw new MappingException("Can't parse timestamp from ${n.getClass}")
    }
  }

  implicit object DateTimeProducer extends Producer[DateTime] {
    def produce(value: DateTime, formatter: OutputFormatter[_]) = formatter.string(value.toString)
  }

  "Joda DateTime" should {
    "serialize to string and deserialize back" in {
      val now = new DateTime
      val nowSerialized = Json4sCodec.from(now)

      val back = Json4sCodec.as[DateTime](nowSerialized)
      /* Note: even if string representations of DateTime objects are the same, the objects may not be
         equal by hashcode. See
         http://stackoverflow.com/questions/21002385/datetime-does-not-equal-itself-after-unserialization
      */
      logger.debug("Joda DateTime now and back: " + now.toString + " " + back.toString)
      back shouldEqual now
    }
  }

  "StackEvt" should {
    "serialize to string and deserialize back" in {
      val testEvent = StackEvt (timestamp = new DateTime)

      val json = Json4sCodec.from(testEvent)
      val back = Json4sCodec.as[StackEvt](json)

      back shouldEqual testEvent
    }
  }
}

divijan avatar Sep 09 '14 11:09 divijan

Similar thing happens with Java Date, only in this case the deserializer expects a JString and fails when it gets anything else.

divijan avatar Sep 09 '14 15:09 divijan

What are the fully qualified class names of formats you are referring to?

divijan avatar Sep 12 '14 11:09 divijan

that was me being confused. I thought this was in the json4s repo not muster

casualjim avatar Sep 12 '14 17:09 casualjim

I have put together a project to illustrate the issue.

divijan avatar Sep 24 '14 08:09 divijan