jsonscalalift-json

Liftweb works in Object but not in Class


This code works:

import net.liftweb.json.{DefaultFormats, parse}

object JsonTest {
  def main(args: Array[String]): Unit = {
    implicit val formats = DefaultFormats // Brings in default date formats etc.
    case class RawObject(filters: List[ChunkObject])
    case class ChunkObject(name: String)

    val json = parse("""{"filters": []}""").extract[RawObject]
    println(json)
  }
}

It prints :

RawObject(List())

This same code within a class does not:

import net.liftweb.json.{DefaultFormats, parse}

class JsonTest2 {
  def fct(): Unit = {
    implicit val formats = DefaultFormats // Brings in default date formats etc.
    case class RawObject(filters: List[ChunkObject])
    case class ChunkObject(name: String)

    val json = parse("""{"filters": []}""").extract[RawObject]
    println(json)
  }
}

object JsonTest2 {
  def main(args: Array[String]): Unit = {
    val jt = new JsonTest2
    jt.fct()
  }
}

Error:

Exception in thread "main" net.liftweb.json.MappingException: Parsed JSON values do not match with class constructor
args=List()
arg types=scala.collection.immutable.Nil$
constructor=public JsonTest2$RawObject$1(JsonTest2,scala.collection.immutable.List)
    at net.liftweb.json.Meta$.fail(Meta.scala:227)
    at net.liftweb.json.Extraction$.instantiate$1(Extraction.scala:274)
    at net.liftweb.json.Extraction$.newInstance$1(Extraction.scala:308)
    at net.liftweb.json.Extraction$.build$1(Extraction.scala:379)
    at net.liftweb.json.Extraction$.extract0(Extraction.scala:444)
    at net.liftweb.json.Extraction$.extract0(Extraction.scala:206)
    at net.liftweb.json.Extraction$.extract(Extraction.scala:43)
    at net.liftweb.json.JsonAST$JValue.extract(JsonAST.scala:703)
    at JsonTest2.fct(JsonTest2.scala:9)
    at JsonTest2$.main(JsonTest2.scala:17)
    at JsonTest2.main(JsonTest2.scala)
Caused by: java.lang.IllegalArgumentException: wrong number of arguments
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at net.liftweb.json.Extraction$.instantiate$1(Extraction.scala:265)
    ... 9 more

What did I miss?

PS: with a simple json (only a field with a string value so without a List inside) it works in both Object and Class

EDIT: I think it is related to the case class definition inside the function but i do not understand why.


Solution

  • From the look of the error messages it seems that the constructor for RawObject takes an additional argument that liftweb is not expecting:

    constructor=public JsonTest2$RawObject$1(JsonTest2,scala.collection.immutable.List)

    RawObject is a nested type, and in Scala nested types are unique for each instance of the containing object. So RawObject inside one instance of JsonTest2 is not the same type as RawObject inside a different instance of JsonTest2.

    I am guessing that Scala places the instance of the containing type (JsonTest2) in the nested type (RawObject) so that it can determine the type at runtime. Hence that JsonTest2 argument to the constructor for RawObject.

    So moving RawObject and ChunkObject out of the JsonTest2 class will fix the problem. These can be placed in the companion object to keep things neat.