I have a case class containing varargs, with an implicit jsonFormat as follows:
import spray.json._
case class Colors(name: String*)
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val colorFormat = jsonFormat1(Colors)
}
import MyJsonProtocol._
Colors("CadetBlue").toJson
It raises an error:
error: type mismatch;
found : Color2.type
required: Seq[String] => Color2
Note: implicit value colorFormat is not applicable here because it comes after the application point and it lacks an explicit result type
implicit val colorFormat = jsonFormat1(Color2)
^
I have also tried:
implicit val colorFormat = jsonFormat1(Colors.apply)
which caused a different (runtime!) exception:
java.lang.RuntimeException: Cannot automatically determine case class field names and order for 'Colors', please use the 'jsonFormat' overload with explicit field name specification
The following:
implicit val colorFormat = jsonFormat(Colors, "name")
raises the former error
It is even possible to define implicit jsonFormat for case class with varargs?
It should work perfectly, probably you have some ambiguity in implicits. This works perfectly:
import spray.json._, DefaultJsonProtocol._
case class Test(param: String*)
object Test {
implicit val testFormat = jsonFormat1(Test.apply)
}
Like a best practice advice, don't use Protocol pattern, it leads to large implicit errors in big project, always define implicits in your companion object (of cause there are exception cases). Another point avoid inheritance, it's not really needed.
Scala *
pattern is just a sugar for Seq
type constructor, so it should find seqFormat
(un)marshaller for this cases.
Update
It doesn't work because Spray uses ClassManifest
to extract field names from the copy
function, but the compiler doesn't generate this function for case classes with varargs in constructor:
case class Test(params: String*)
def man[A: ClassManifest] = implicitly[ClassManifest[A]]
man[Test].erasure.getDeclaredMethods.filter(_.getName.contains("copy"))
res4: Array[java.lang.reflect.Method] = Array()
scala> case class Test(param: String)
defined class Test
scala> man[Test].erasure.getDeclaredMethods.filter(_.getName.contains("copy"))
warning: there was one deprecation warning; re-run with -deprecation for details
res5: Array[java.lang.reflect.Method] = Array(public Test Test.copy(java.lang.String), public java.lang.String Test.copy$default$1())
So you need to provide the field name manually. BTW i didn't know this before