Skip to content
This repository has been archived by the owner on Nov 28, 2017. It is now read-only.

Json4sCodec deserializer expects an object to deserialize joda DateTime from #14

Open
divijan opened this issue Sep 3, 2014 · 7 comments

Comments

@divijan
Copy link

divijan commented Sep 3, 2014

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.

@casualjim
Copy link
Member

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

@casualjim
Copy link
Member

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

@divijan
Copy link
Author

divijan commented Sep 9, 2014

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
Copy link
Author

divijan commented Sep 9, 2014

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

@divijan
Copy link
Author

divijan commented Sep 12, 2014

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

@casualjim
Copy link
Member

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

@divijan
Copy link
Author

divijan commented Sep 24, 2014

I have put together a project to illustrate the issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

2 participants