[spray-json] How to Define JSON Format for scala.concurrent.duration.FiniteDuration?

1,445 views
Skip to first unread message

kra...@gmail.com

unread,
Feb 22, 2013, 3:00:07 AM2/22/13
to spray...@googlegroups.com
Greetings, I am trying to serialize HttpServer.Stats as JSON using spray-json. The problem is that FiniteDuration has a companion object so I read up on how to create a format for it and came up with the following:

After some research I tried the following:

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val formatFiniteDuration = jsonFormat(FiniteDuration.apply, "length", "unit")
  implicit val formatHttpServerStats = jsonFormat9(HttpServer.Stats)
}

Problem is the first format gives an ambiguous reference to apply (since there are two) and the following doesnt work either:

  implicit val formatFiniteDuration = jsonFormat(FiniteDuration.apply(_:Long, _:String), "length", "unit")

Thats me trying to figure out a way to clarify the ambiguity but then I get:

/ecplat/src/main/scala/com/kraythensoft/ecplat/Actors.scala:21: inferred type arguments [Long,String,scala.concurrent.duration.FiniteDuration] do not conform to method jsonFormat's type parameter bounds [A,B,T <: Product]
  implicit val formatFiniteDuration = jsonFormat(FiniteDuration.apply(_:Long, _:String), "length", "unit")

Perhaps i am doing something wrong in the disambiguation?

Is there a better way to do this that I am not seeing?

Also inside FiniteDuration there is a reference to scala.concurrent.TimeUnit which contains some scala constructs I dont understand. How would I serialize them?

  type TimeUnit          = java.util.concurrent.TimeUnit
  final val DAYS         = java.util.concurrent.TimeUnit.DAYS
  final val HOURS        = java.util.concurrent.TimeUnit.HOURS
  // and so on ...

I gather the type line is a bit like a c++ typedef and the other lines are creating package objects that reflect constants in the backing Java class. No idea how to yank them into the serialization.

Thanks in advance

Mathias

unread,
Feb 22, 2013, 3:27:23 AM2/22/13
to spray...@googlegroups.com
The problem is that FiniteDuration (in Scala 2.10) is not a case class.
Therefore you cannot use the `jsonFormat` helper to construct a JsonFormat for it.
You'll have to resort to the "manual" approach…

Cheers,
Mathias

---
mat...@spray.io
http://spray.io
> --
> You received this message because you are subscribed to the Google Groups "spray-user" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to spray-user+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

kra...@gmail.com

unread,
Feb 22, 2013, 4:44:31 AM2/22/13
to spray...@googlegroups.com
Thanks for the information, if altogether disappointing. Does this mean i have to render the stats to a map or something first and then output the map or can I define a spray-json formatter manually that will pick it up? If so, how do I define it?

-- Robert

Mathias

unread,
Feb 22, 2013, 4:50:33 AM2/22/13
to spray...@googlegroups.com
Robert,

this section of the spray-json docs should show you how to do it:
https://github.com/spray/spray-json#providing-jsonformats-for-other-types

Cheers,
Mathias

---
mat...@spray.io
http://spray.io

kra...@gmail.com

unread,
Feb 22, 2013, 1:18:33 PM2/22/13
to spray...@googlegroups.com
Thanks a bunch man.

T.J. Corrigan

unread,
Mar 11, 2015, 4:48:51 PM3/11/15
to spray...@googlegroups.com
In case someone else comes across this here is the code you will need:


  implicit object finiteDurationFormat extends RootJsonFormat[FiniteDuration] {
    def write(fd: FiniteDuration) = JsObject(
      "length" -> JsNumber(fd.length),
      "unit"   -> JsString(fd.unit.name())
    )
      
    def read(value: JsValue) = {
      value.asJsObject.getFields("length", "unit") match {
        case Seq(JsNumber(length), JsString(unit)) =>
          FiniteDuration(length.toLong, unit)
        case _ => throw new DeserializationException("FiniteDuration expected")
      }
    }
  }

  implicit val statsFormat: RootJsonFormat[Stats] = jsonFormat8(Stats.apply)


Note the ordering of the implicits matters.
Reply all
Reply to author
Forward
0 new messages