NietzscheanAI
NietzscheanAI

Reputation: 966

Spray JSON can't serialise case class

I am attempting to convert the following case class to/from JSON using Spray:

case class Interval(lower: Int, upper: Int)

This is achieved via:

implicit val intervalFormat = jsonFormat2(Interval)

This compiles, but gives the runtime error:

Cannot automatically determine case class field names and order for 'Interval', please use the 'jsonFormat' overload with explicit field name specification

A search on this error suggests that it typically arises when subclasses declare additional fields, which is not the case here.

Am I mistaken in thinking that Spray should be able to automatically format the interval class?

If so, then (as the error message appears to suggest) should I be providing a formatter with more explicit information about the fields of Interval? How might this most readily be achieved?

EDIT: The answer by @retrospectacus offers some helpful points, but none of them solve the problem. The workaround I adopted was to provide an explicit description of the types and names of the fields:

implicit val intervalFormat = jsonFormat[Int, Int,Interval](Interval, "lower", "upper")

This works, but I'm leaving the question open, since it's still not clear why this is necessary.

Upvotes: 1

Views: 1122

Answers (2)

Andrii Lashchenko
Andrii Lashchenko

Reputation: 1

I had the same error for a case class defined inside a class. E.g.

class TestClass {
  case class TestModel(a: String, b: Int, c: Long)

  trait TestJsonProtocol extends DefaultJsonProtocol {
    implicit def testJf: RootJsonFormat[TestModel] = jsonFormat3(TestModel.apply)
  }

// ...

}

was producing an error during compilation java.lang.RuntimeException: Cannot automatically determine case class field names and order for 'TestClass$TestModel', please use the 'jsonFormat' overload with explicit field name specification

The issue was solved once I moved TestModel to TestClass companion object:

object TestClass {
  case class TestModel(a: String, b: Int, c: Long)

  trait TestJsonProtocol extends DefaultJsonProtocol {
    implicit def testJf: RootJsonFormat[TestModel] = jsonFormat3(TestModel.apply)
  }
}

class TestClass {
  import TestClass._
  // ...
}

I hope this will be useful.

Upvotes: 0

retrospectacus
retrospectacus

Reputation: 2586

Common reasons for this error:

  • If you have a case object Interval (companion) somewhere, then the jsonFormat should be created like jsonFormat2(Interval.apply).
  • Adding a type annotation to the format can help: implicit val intervalFormat: RootJsonFormat[Interval] = ...
  • Your Interval class could be confused with another Interval class, possibly org.joda.time.Interval, either in the jsonFormat creation or in the place of case class creation or serialization - you may add a prefix or fix imports to avoid this.

Hope this helps.

EDIT: Another reason for this issue which I just encountered, is if you have any val declared within the case class, the serializer won't be able to automatically generate a format. E.g.

case class Interval(from: Int, to: Int) {
  val size: Int = to - from
}

The error will be "java.lang.RuntimeException: Case class Interval declares additional fields".

This can be solved using the jsonFormat overload as described above, or the val can be simply changed to a def.

Upvotes: 9

Related Questions