I have a simple case class:
object Margin {
def apply(top: Int, right: Int, bottom: Int, left: Int): Margin = {
Margin(Some(top), Some(right), Some(bottom), Some(left))
}
}
case class Margin(top: Option[Int], right: Option[Int], bottom: Option[Int], left: Option[Int])
When calling upickle.write
on an instance of the above class, I get the following exception:
scala.ScalaReflectionException: value apply encapsulates multiple
overloaded alternatives and cannot be treated as a method. Consider
invoking `<offending symbol>.asTerm.alternatives` and manually picking
the required method
What does this error message mean and how do I fix it?
The above error message is a result of the Margin
class having multiple overloaded apply
methods. The first one is the case class constructor, and the other is in the companion object. Upickle does not know which apply
method to use and thus throws this exception. This is a known limitation.
One workaround is to rename the apply
method that is in the companion object. Another is to write a custom pickler.
Here is a somewhat clumsy version of a custom pickler that solved the problem:
object Margin {
def create(top: Int, right: Int, bottom: Int, left: Int): Margin = {
Margin(Some(top), Some(right), Some(bottom), Some(left))
}
implicit val marginWriter = upickle.Writer[Margin]{
case m =>
Js.Obj(fields(m).map(kv => (kv._1, Js.Num(kv._2))):_*).asInstanceOf[Js.Value]
}
implicit val marginReader = upickle.Reader[Margin]{
case obj: Js.Obj =>
val map = obj.value.toMap
Margin(map.get("top").map(_.value.asInstanceOf[Int]),
map.get("right").map(_.value.asInstanceOf[Int]),
map.get("bottom").map(_.value.asInstanceOf[Int]),
map.get("left").map(_.value.asInstanceOf[Int]))
}
private def fields(m: Margin) = Seq(m.top.map(("top", _)), m.right.map(("right", _)), m.bottom.map(("bottom", _)),
m.left.map(("left", _))).flatten
}