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?
I also experienced this exception, however in my case it was due to a 'val' definition within the case class (not as passed argument).
Problematic case class:
case class Color(name: String) {
val code: Int = ...
}
Working case class:
case class Color(name: String) {
def code: Int = ...
}
Which is kind of misfortunate as, now the 'code' will be computed on each call.
(Used Akka Http Version - 10.1.1)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With