简体   繁体   中英

JSON to case class with generics decoding with json4s

I'm trying to build a json to case class en/decoder using json4s Using generics icw Manifest seems to work for ordinary types/classes, but more complex configs seem to fail.

How can I use json4s combined with extracting more complex types from json strings?

import org.json4s._
import org.json4s.native.JsonMethods._
implicit val formats = org.json4s.DefaultFormats

case class User(name:String)
case class Product(id:String)


case class Meta(count:Int)
case class ResultList[T: Manifest](meta: Meta, result: List[T])


// Without generics
case class ResultListUser(meta: Meta, result: List[User])
case class ResultListProduct(meta: Meta, result: List[Product])


// general decode method
def decode[T: Manifest](jsonStr: String): T = {
  parse(jsonStr).extract[T]
}


// data
val userJson = """{"meta":{"count":2},"result":[{"name":"Tom"},{"name":"Lucas"}]}"""
val productJson = """{"meta":{"count":2},"result":[{"id":"123"},{"id":"456"}]}"""


val resultListUser = decode[ResultListUser](userJson)

resultListUser: ResultListUser = ResultListUser(Meta(2),List(User(Tom), User(Lucas)))

val resultListProduct = decode[ResultListProduct](productJson)

resultListProduct: ResultListProduct = ResultListProduct(Meta(2),List(Product(123), Product(456)))

val resultListUser2 = decode[ResultList[User]](userJson)

org.json4s.package$MappingException: No usable value for evidence$1

No constructor for type Manifest[User], JNothing

...

val resultListProduct2 = decode[ResultList[Product]](productJson)

org.json4s.package$MappingException: No usable value for evidence$1 No constructor for type Manifest[Product], JNothing at org.json4s.reflect.package$.fail(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:91) at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$buildCtorArg(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:522) at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$15.apply(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:542) at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$15.apply(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:542) at scala.collection.TraversableLike$$anonfun$map$1.apply(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:230) at scala.collection.TraversableLike$$anonfun$map$1.apply(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:230) at scala.collection.mutabl e.ResizableArray$class.foreach(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:55) at scala.collection.mutable.ArrayBuffer.foreach(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:44) at scala.collection.TraversableLike$class.map(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:230) at scala.collection.AbstractTraversable.map(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:100) at org.json4s.Extraction$ClassInstanceBuilder.instantiate(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:542) at org.json4s.Extraction$ClassInstanceBuilder.result(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:593) at org.json4s.Extraction$$anonfun$extract$6.apply(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:396) at org.json4s.Extraction$$anonfun$extract$6.apply(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgeneric s.sc:388) at org.json4s.Extraction$.customOrElse(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:602) at org.json4s.Extraction$.extract(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:388) at worksheet.worksheet(/Users/tomlous/Development/Scala/testjes/src/main/scala/json4sgenerics.sc:35)

Caused by: org.json4s.package$MappingException: No constructor for type Manifest[Product], JNothing at org.json4s.reflect.package$.fail(package.scala:95) at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$org$json4s$Extraction$ClassInstanceBuilder$$constructor$1.apply(Extraction.scala:477) at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$org$json4s$Extraction$ClassInstanceBuilder$$constructor$1.apply(Extraction.scala:477) at scala.Option.getOrElse(Option.scala:121) at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$constructor(Extraction.scala:477) at org.json4s.Extraction$ClassInstanceBuilder.instantiate(Extraction.scala:532) at org.json4s.Extraction$ClassInstanceBuilder.result(Extraction.scala:597) at org.json4s.Extraction$$anonfun$extract$6.apply(Extraction.scala:400) at org.json4s.Extraction$$anonfun$extract$6.apply(Extraction.scala:392) at org.json4s.Extraction$.customOrElse(Extraction.scala:606) at org.json4s.Extraction$.extract(Ex traction.scala:392) at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$buildCtorArg(Extraction.scala:514) at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$15.apply(Extraction.scala:546) at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$15.apply(Extraction.scala:546) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234) at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59) at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48) at scala.collection.TraversableLike$class.map(TraversableLike.scala:234) at scala.collection.AbstractTraversable.map(Traversable.scala:104) at org.json4s.Extraction$ClassInstanceBuilder.instantiate(Extraction.scala:546) at org.json4s.Extraction$ClassInstanceBuilder.result(Extraction.scala:597) at org.json4s.Extraction$$anonfun$extract$6.apply(Extraction.scala:4 00) at org.json4s.Extraction$$anonfun$extract$6.apply(Extraction.scala:392) at org.json4s.Extraction$.customOrElse(Extraction.scala:606) at org.json4s.Extraction$.extract(Extraction.scala:392) at org.json4s.Extraction$.extract(Extraction.scala:39) at org.json4s.ExtractableJsonAstNode.extract(ExtractableJsonAstNode.scala:21) at A$A7$A$A7.decode(json4sgenerics.sc:24) at A$A7$A$A7.resultListProduct2$lzycompute(json4sgenerics.sc:37) at A$A7$A$A7.resultListProduct2(json4sgenerics.sc:37) at A$A7$A$A7.get$$instance$$resultListProduct2(json4sgenerics.sc:36) at A$A7$.main(json4sgenerics.sc:92) at A$A7.main(json4sgenerics.sc) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.jetbrains.plugins.scala.worksheet.MyWorksheetRunner.main(MyWorksheetRunner.java:22)

The problem is that you do want Manifest to be passed to decode but you probably don't want it in

case class ResultList[T: Manifest](meta: Meta, result: List[T])

The thing is that this code is actually compiled to something like

case class ResultList[T](meta: Meta, result: List[T])(implicit evidence: Manifest[T])

and this implicit parameter evidence is exactly what json4s can't figure out how to provide for you from your JSON (and at this point it can't use implicit resolution as well because it is done only at compile time).

So if you change your ResultList to

case class ResultList[T](meta: Meta, result: List[T])

I expect that yout code will work as long as T is bound to something that Json4s can extract.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM