Search code examples
scalajacksonjerkson

Why can't I parse this JSON string with a custom Scala type (Jerkson)?


I've got the following code that is meant to parse the following JSON string with Jerkson, the Scala wrapper for Jackson:

Code: val vertex = Json.parse[Ranked[UserVertex]](rawString)

Json: "originalPos":"515693049","rankScore":1.2409032357406248E8,"ranked":{"userId":"101010110","userName":"John","channel":"thirtyfive"}}

The following JSON is represented by this Scala class:

case class Ranked[T] (
  val originalPos: String,

  val rankScore: Double,

  val ranked: T
)

and T is a UserVertex class with the following fields above (userId, userName, channel). When I try to parse the JSON string I get the following error:

[error] application - Could not parse the string:
{"originalPos":"655330261","rankScore":1.2423105672295567E8,"ranked":{"userId":"655330261","userName":"Sheile Mercado-Mallari","channel":"facebook"}}
java.lang.ClassNotFoundException: T
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at com.codahale.jerkson.util.CaseClassSigParser$.loadClass(CaseClassSigParser.scala:153)

It looks like the ClassLoader can't find the class, but it's definitely loaded. Any idea what the root cause of this is? Do I have some sort of multiple class loader issue here? Thanks!


Solution

  • In this case (a generic case class) you need to define your reader (It is a known constraint in Play)

    case class UserVertex(userId:String, userName: String, channel: String)
    case class Ranked[T](originalPos: String, rankScore: Double, ranked: T)
    

    Note that you don't need val keyword in case classes as it will be automatically provided by using the case keyword.

    import play.api.libs.json._
    import play.api.libs.functional.syntax._
    
    implicit def vertexReader: Reads[UserVertex] = Json.reads[UserVertex]
    
    implicit def rankedReader[T](implicit reads: Reads[T]): Reads[Ranked[T]] = (
        (__ \ "originalPos").read[String] ~ 
        (__ \ "rankScore").read[Double] ~ 
        (__ \ "ranked").read[T]
        )(Ranked.apply[T] _)
    

    Now you can convert the following raw json to Ranked

    val rawString =
        """{
          |  "originalPos": "515693049",
          |  "rankScore": 1.2409032357406248E8,
          |  "ranked": {
          |    "userId": "101010110",
          |    "userName": "John",
          |    "channel": "thirtyfive"
          |  }
          |}""".stripMargin
    

    with the following code

    val rankedJson = Json.parse(rawString)
    val ranked = Json.fromJson[Ranked[UserVertex]](rankedJson).get
    

    Disclaimer

    Tested with Play 2.3.5 and Scala 2.11.4