Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

read json throws NullPointerException with Scala3 #371

Closed
otto-ringhofer opened this issue Nov 29, 2021 · 2 comments
Closed

read json throws NullPointerException with Scala3 #371

otto-ringhofer opened this issue Nov 29, 2021 · 2 comments

Comments

@otto-ringhofer
Copy link

Reading json generated by write throws NullPointerException with Scala3, the same code with Scala2.13 runs without problems.

build.sbt

scalaVersion := "3.1.0" // 2.13.7 works correct, 3.1.0 crashes
libraryDependencies ++= Seq("com.lihaoyi" %% "upickle" % "1.4.2")

Code reproducing the problem

import upickle.default._

object ShowCrash {
  case class Node(name: String, kids: Vector[Node])

  implicit val nodeRW: ReadWriter[Node] = macroRW[Node]

  val ndb = Node("b", Vector.empty)
  val nda = Node("a", Vector(ndb))

  def main(args: Array[String]): Unit = {
    println(s"nda: $nda")
    val json = write(nda)
    println(s"json: $json")
    val node = read[Node](json)
    println(s"node: $node")
  }
}

Fails with:

nda: Node(a,Vector(Node(b,Vector())))
json: {"name":"a","kids":[{"name":"b","kids":[]}]}
[error] (run-main-1) java.lang.NullPointerException
[error] java.lang.NullPointerException
[error]         at ujson.CharParser.parseNested(CharParser.scala:401)
lolgab added a commit to lolgab/upickle that referenced this issue Jan 8, 2022
lolgab added a commit to lolgab/upickle that referenced this issue Jan 8, 2022
@edwardcwang
Copy link

Another example with Scala 3.1.1 and upickle 2.0.0; not sure if it's the same etiology or not.

Stacktrace:

java.lang.NullPointerException
  ujson.CharParser.visitString(CharParser.scala:617)
  ujson.CharParser.parseStringValue(CharParser.scala:586)
  ujson.CharParser.parseNested(CharParser.scala:379)
  ujson.CharParser.parseTopLevel0(CharParser.scala:324)
  ujson.CharParser.parseTopLevel(CharParser.scala:308)
  ujson.CharParser.parse(CharParser.scala:59)
  ujson.StringParser$.transform(StringParser.scala:28)
  ujson.StringParser$.transform(StringParser.scala:28)
  ujson.Readable$fromTransformer.transform(Readable.scala:13)
  upickle.Api.read$$anonfun$1(Api.scala:37)
  upickle.core.TraceVisitor$.withTrace(TraceVisitor.scala:18)
  upickle.Api.read(Api.scala:37)
  upickle.Api.read$(Api.scala:17)
  upickle.default$.read(Api.scala:157)

Code:

import upickle.default._

case class TestClass(val map: Map[String, Boolean | Int])
object TestClass {
  implicit val rw: upickle.default.ReadWriter[TestClass] =
    upickle.default.macroRW
  implicit val unionRw: upickle.default.ReadWriter[Boolean | Int] =
    upickle.default
      .readwriter[String]
      .bimap[Boolean | Int](
        x => {
          println(s"DEBUG: x = ${x.toString}")
          x.toString
        },
        a => {
          println(s"DEBUG: a = ${a}")
          a match {
            case "true"  => true
            case "false" => false
            case x       => x.toInt
          }
        }
      )
}

def main(): Unit = {
  val myStr = """
{"map":{
  "x":"1","y":"-2","z":"true"
}}
"""

  // Bad
  upickle.default.read[TestClass](myStr)

  // OK
  upickle.default.read[Map[String, Map[String, String]]](myStr)

  val myObj = TestClass(Map("x" -> 1, "y" -> -2, "z" -> true))

  // Bad
  upickle.default.read[TestClass](
    upickle.default.write(myObj)
  )

  // OK
  upickle.default.read[Map[String, Map[String, String]]](
    upickle.default.write(myObj)
  )
}

@Cutkh
Copy link

Cutkh commented Aug 10, 2022

I've experienced the same issue and found a (even more Scala3 idiomatic) workaround: replace the implicit val with a given.
This works at least for the first example above, as well as the code below.

I'm not sure if this is a compiler or a macro issue.

In the code below it actually is sufficient to replace only one implicit val with a given and leave the other be.

final case class Status(statusDetail: Detail)
enum Detail:
  case NoDetail, SomeDetail

// given rwStatus : ReadWriter[Status] = macroRW

// given rwDetail: ReadWriter[Detail] =
//   readwriter[Int].bimap[Detail](_.ordinal, Detail.fromOrdinal)

implicit val rwStatus : ReadWriter[Status] = macroRW

implicit val rwDetail: ReadWriter[Detail] = readwriter[Int].bimap[Detail](_.ordinal, Detail.fromOrdinal)

object TestUpickle:

  def main(args : Array[String]) =
      val someValue = Status(Detail.NoDetail)
      println(write(someValue))
      //Throws NPE when using implicits but works for given
      println(read[Status](write(someValue)))
end TestUpickle

Versions:

JVM version: 18.0.2
Scala version:  3.1.3
Upickle: 2.0.0

Thrown Exception:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "upickle.core.Visitor.visitFloat64StringParts(java.lang.CharSequence, int, int, int)" because "facade" is null
	at ujson.CharParser.visitFloat64StringPartsWithWrapper(CharParser.scala:161)
CharParser.scala:161
	at ujson.CharParser.parseNum(CharParser.scala:148)
CharParser.scala:148
	at ujson.CharParser.parseNested(CharParser.scala:409)
CharParser.scala:409
	at ujson.CharParser.parseTopLevel0(CharParser.scala:324)
CharParser.scala:324
	at ujson.CharParser.parseTopLevel(CharParser.scala:308)
CharParser.scala:308
	at ujson.CharParser.parse(CharParser.scala:59)
CharParser.scala:59
2
	at ujson.StringParser$.transform(StringParser.scala:28)
StringParser.scala:28
	at ujson.Readable$fromTransformer.transform(Readable.scala:13)
Readable.scala:13
	at upickle.Api.read$$anonfun$1(Api.scala:37)
Api.scala:37
	at upickle.core.TraceVisitor$.withTrace(TraceVisitor.scala:18)
TraceVisitor.scala:18
	at upickle.Api.read(Api.scala:37)
Api.scala:37
	at upickle.Api.read$(Api.scala:17)
Api.scala:17
	at upickle.default$.read(Api.scala:157)
Api.scala:157
	at programs.TestUpickle$.main(TestUpickle.scala:26)
TestUpickle.scala:26
	at programs.TestUpickle.main(TestUpickle.scala)

lihaoyi added a commit that referenced this issue Feb 27, 2023
lihaoyi added a commit that referenced this issue Feb 27, 2023
@lihaoyi lihaoyi closed this as completed Feb 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants