I tested out the serialization of a Scala case class using Jackson.
DeserializeTest.java
public static void main(String[] args) throws Exception { // being lazy to catch-all
final ObjectMapper mapper = new ObjectMapper();
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
mapper.writeValue(stream, p.Foo.personInstance());
System.out.println("result:" + stream.toString());
}
}
Foo.scala
object Foo {
case class Person(name: String, age: Int, hobbies: Option[String])
val personInstance = Person("foo", 555, Some("things"))
val PERSON_JSON = """ { "name": "Foo", "age": 555 } """
}
When I ran the above main
of the Java class, an exception was thrown:
[error] Exception in thread "main" org.codehaus.jackson.map.JsonMappingException:
No serializer found for class p.Foo$Person and no properties discovered
to create BeanSerializer (to avoid exception,
disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) )
How can I (de)-serialize Scala case classes?
Jackson is expecting your class to be a JavaBean, which means its expects the class to have a getX() and/or setX() for every property.
Option 1
You can create JavaBean classes in Scala using the annotation BeanProperty.
Example
case class Person(
@BeanProperty val name: String,
@BeanProperty val age: Int,
@BeanProperty val hobbies: Option[String]
)
In this case a val will mean only a getter is defined. If you want setters for deserialization you defined the properties as var.
Option 2
While option 1 will work, if you really want to use Jackson there are wrappers that allow it to deal with Scala classes like FasterXML's scala module which might be a better approach. I haven't used it as I've just been using the Json library built in to play.
Found a solution that works with jackson and scala case classes.
I used a scala module for jackson - jackson-module-scala.
libraryDependencies ++= Seq(
"com.fasterxml.jackson.core" % "jackson-databind" % "2.5.3",
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.2.2"
)
I had to annotate fields in my case class with @JsonProperty.
This is what my case class looks like:
case class Person(@JsonProperty("FName") FName: String, @JsonProperty("LName") LName: String)
And this is how I deserialize:
val objectMapper = new ObjectMapper() with ScalaObjectMapper
objectMapper.registerModule(DefaultScalaModule)
val str = """{"FName":"Mad", "LName": "Max"}"""
val name:Person = objectMapper.readValue[Person](str)
Serialization is easier:
val out = new ByteArrayOutputStream()
objectMapper.writeValue(out, name)
val json = out.toString
Would like to clarify that I am using
com.fasterxml.jackson.databind.ObjectMapper
In the question, it seems he is using
org.codehaus.jackson.map.ObjectMapper
which won't work with ScalaObjectMapper.
Based on Priyank Desai's answer I have created a generic function to convert json string
to case class
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
def jsonToType[T](json:String)(implicit m: Manifest[T]) :T = {
val objectMapper = new ObjectMapper() with ScalaObjectMapper
objectMapper.registerModule(DefaultScalaModule)
objectMapper.readValue[T](json)
}
Usage:
case class Person(@JsonProperty("name") Name:String, @JsonProperty("age") Age:Int)
val personName = jsonToType[Person](jsonString).name