Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

json4s Serialize and deserialize generic type

Tags:

scala

Because I am dealing with generic type therefore I can't use specific case classes. Then I created a generic util which serializes and deserializes generic object.

import org.json4s
import org.json4s.Formats._
import org.json4s.native.JsonMethods._

object JsonHelper {
  def json2Object[O](input: String) : O = {
    parse(json4s.string2JsonInput(input)).asInstanceOf[O]
  }
  def object2Json[O](input: O) : String = {
    write(input).toString
  }
}

The compiler throws the error:

No JSON serializer found for type O. Try to implement an implicit Writer or JsonFormat for this type. write(input).toString

This should be thrown at runtime but why it's thrown at compile time?

like image 811
Quy Tang Avatar asked Oct 06 '16 16:10

Quy Tang


3 Answers

In a comment above, you asked "So how jackson can work with java object? It use reflection right? And why it's different from Scala?", which gets to the heart of this question.

The json4s "native" serializer you have imported uses compile-time reflection to create the Writer.

Jackson uses run-time reflection to do the same.

The compile-time version is more efficient; the run-time version is more flexible.

To use the compile-time version, you need to let the compiler have enough information to choose the correct Writer based on the declared type of the object to be serialized. This will rule out very generic writer methods like the one you propose. See @TimP's answer for how to fix your code for that version.

To use the run-time version, you can use Jackson via the org.json4s.jackson.JsonMethods._ package. See https://github.com/json4s/json4s#jackson

like image 76
Rich Avatar answered Oct 21 '22 05:10

Rich


The compiler error you posted comes from this location in the json4s code. The write function you're calling takes an implicit JSON Writer, which is how the method can take arbitrary types. It's caught at compile time because implicit arguments are compiled the same way explicit ones are -- it's as if you had:

def f(a: Int, b: Int) = a + b
f(5) // passed the wrong number of arguments

I'm having a bit of trouble seeing exactly which write method you're calling here -- the json4s library is pretty big and things are overloaded. Can you paste the declared write method you're using? It almost certainly has a signature like this:

def write[T](value: T)(implicit writer: Writer[T]): JValue

If it looks like the above, try including the implicit writer parameter in your method as so:

object JsonHelper {
  def json2Object[O](input: String)(implicit reader: Reader[O]) : O = {
    parse(json4s.string2JsonInput(input)).asInstanceOf[O]
  }
  def object2Json[O](input: O)(implicit writer: Writer[O]) : String = {
    write(input).toString
  }
}
like image 28
Tim Avatar answered Oct 21 '22 04:10

Tim


In this example, you have a deal with generic types, Scala, like another jvm languages, has type erasing mechanism at compile time (error message at compile time may don't contain message about generic in a whole), so try to append this fragment to the signature of both methods:

(implicit tag: ClassTag[T])

it's similar to you example with generic, but with jackson. HTH

like image 1
Artem Avatar answered Oct 21 '22 03:10

Artem