com-lihaoyi / upickle

uPickle: a simple, fast, dependency-free JSON & Binary (MessagePack) serialization library for Scala
https://com-lihaoyi.github.io/upickle
MIT License
715 stars 164 forks source link

NoSuchElementException: None.get on sealed trait deserialization with `$type` tag #500

Closed szymon-rd closed 1 year ago

szymon-rd commented 1 year ago

Version

3.1.0

Behaviour

Following code throws a java.util.NoSuchElementException: None.get


//> using lib "com.lihaoyi::upickle:3.1.0"

import upickle.default.*
import upickle.implicits.*

sealed trait ClusterEvent derives ReadWriter {
  val tpe: String
}

case class MemberUp(tpe: String) extends ClusterEvent derives ReadWriter

val str =
  """
{
  "tpe": "MemberUp"
}
  """.trim
@main
def main = 
    println(read[ClusterEvent](str))

Full stack trace:

Exception in thread "main" java.util.NoSuchElementException: None.get
        at scala.None$.get(Option.scala:627)
        at scala.None$.get(Option.scala:626)
        at upickle.AttributeTagged$$anon$6.visitEnd(Api.scala:281)
        at ujson.CharParser.liftedTree1$1(CharParser.scala:512)
        at ujson.CharParser.tryCloseCollection(CharParser.scala:512)
        at ujson.CharParser.parseNested(CharParser.scala:478)
        at ujson.CharParser.parseTopLevel0(CharParser.scala:339)
        at ujson.CharParser.parseTopLevel(CharParser.scala:323)
        at ujson.CharParser.parse(CharParser.scala:72)
        at ujson.StringParser$.transform(StringParser.scala:28)
        at ujson.StringParser$.transform(StringParser.scala:28)
        at ujson.Readable$fromTransformer.transform(Readable.scala:16)
        at upickle.Api.read$$anonfun$1(Api.scala:38)
        at upickle.core.TraceVisitor$.withTrace(TraceVisitor.scala:18)
        at upickle.Api.read(Api.scala:38)
        at upickle.Api.read$(Api.scala:17)
        at upickle.default$.read(Api.scala:158)
        at Test$package$.main(Test.scala:21)
        at main.main(Test.scala:19)

Expected behaviour

Either working deserialization or a clear library error. It shows two issues: One is the None.get, and the other is that it is hard to customize how the case class inheriting the sealed trait is selected.

lihaoyi commented 1 year ago

Your example is missing the $type tag, but we should provide a better error message regardless