upickle gives a ScalaReflectionException when writing a case class

585 views Asked by At

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?

1

There are 1 answers

0
Julie On BEST ANSWER

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
}