Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

uPickle and ScalaJS: sealed trait serialisation

I'm trying to get a basic upickle example to work and it seems I'm missing something here. I want to try out the example provided on the readme page for upickle

import upickle._

sealed trait A
@key("Bee") case class B(i: Int) extends A
case object C extends A

Then, my code is:

object Model {
  def main(args: Array[String]): Unit = {

    val a = B(5): A
    println(a)

    val out = write(a)
    println(out)

    val a2 = read[A](out)
    println(a2)

    println(a == a2)

  }
}

All I get is the error:

The referenced trait [[A]] does not have any sub-classes. This may happen due to a limitation of scalac (SI-7046) given that the trait is not in the same package. If this is the case, the hierarchy may be defined using integer constants.

I have two questions:

  • How can I convince uPickle that the trait is in the same package? (Because it is.)
  • Or if I can't: how can I define the hierarchy with integer constants?
like image 718
Jean-Philippe Pellet Avatar asked Feb 20 '15 14:02

Jean-Philippe Pellet


Video Answer


2 Answers

Ever had the syndrome that you can spend multiple hours on a problem like this, and you solve it minutes after asking the StackOverflow question?

It turns out that due to compiler-specific details, such a sealed trait won't know its direct subclasses until after the point in the file where it's defined. So, in this case, I had defined the trait and its cases after the main method, where upickle would do its macro expansion and magic. At this point, it won't know about the trait's cases. If the main method is moved in the same file after the definition of the trait and its cases, it will work.

like image 133
Jean-Philippe Pellet Avatar answered Sep 22 '22 15:09

Jean-Philippe Pellet


I encountered this error in a Scala.js project where I shared a sealed abstract class between server and client.

The solution was to use the Typelevel compiler instead of the default Lightbend one.

This is easily done:

  1. Put this in your build.properties: sbt.version=0.13.13-M1
  2. and this at the top of your build.sbt: scalaOrganization in ThisBuild:= "org.typelevel"

For completeness' sake, this is the class I can now share between client and server:

sealed abstract class ServerResponse

case class Ok(msg: String) extends ServerResponse

case class Failure(msg: String) extends ServerResponse
like image 34
Matthias Braun Avatar answered Sep 20 '22 15:09

Matthias Braun