Há um bom módulo adicional para Jackson para suportar tipos de dados Scala. Estas são as dependências:
libraryDependencies += "com.fasterxml.jackson.core" % "jackson-databind" % "2.2.2",
libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.2.2"
Eu escrevi um pequeno pacote de conveniência em torno dele para demonstrar seu uso
import com.fasterxml.jackson.databind.{DeserializationFeature, ObjectMapper}
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
object JsonUtil {
val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
def toJson(value: Map[Symbol, Any]): String = {
toJson(value map { case (k,v) => k.name -> v})
}
def toJson(value: Any): String = {
mapper.writeValueAsString(value)
}
def toMap[V](json:String)(implicit m: Manifest[V]) = fromJson[Map[String,V]](json)
def fromJson[T](json: String)(implicit m : Manifest[T]): T = {
mapper.readValue[T](json)
}
}
Agora você pode (des) organizar facilmente qualquer estrutura JSON válida:
/*
* (Un)marshalling a simple map
*/
val originalMap = Map("a" -> List(1,2), "b" -> List(3,4,5), "c" -> List())
val json = JsonUtil.toJson(originalMap)
// json: String = {"a":[1,2],"b":[3,4,5],"c":[]}
val map = JsonUtil.toMap[Seq[Int]](json)
// map: Map[String,Seq[Int]] = Map(a -> List(1, 2), b -> List(3, 4, 5), c -> List())
/*
* Unmarshalling to a specific type of Map
*/
val mutableSymbolMap = JsonUtil.fromJson[collection.mutable.Map[Symbol,Seq[Int]]](json)
// mutableSymbolMap: scala.collection.mutable.Map[Symbol,Seq[Int]] = Map('b -> List(3, 4, 5), 'a -> List(1, 2), 'c -> List())
/*
* (Un)marshalling nested case classes
*/
case class Person(name: String, age: Int)
case class Group(name: String, persons: Seq[Person], leader: Person)
val jeroen = Person("Jeroen", 26)
val martin = Person("Martin", 54)
val originalGroup = Group("Scala ppl", Seq(jeroen,martin), martin)
// originalGroup: Group = Group(Scala ppl,List(Person(Jeroen,26), Person(Martin,54)),Person(Martin,54))
val groupJson = JsonUtil.toJson(originalGroup)
// groupJson: String = {"name":"Scala ppl","persons":[{"name":"Jeroen","age":26},{"name":"Martin","age":54}],"leader":{"name":"Martin","age":54}}
val group = JsonUtil.fromJson[Group](groupJson)
// group: Group = Group(Scala ppl,List(Person(Jeroen,26), Person(Martin,54)),Person(Martin,54))
Se você realmente quer se divertir, você pode até mesmo fazer um monkey patch para as classes Scala para simplificar (des) marshalling
object MarshallableImplicits {
implicit class Unmarshallable(unMarshallMe: String) {
def toMap: Map[String,Any] = JsonUtil.toMap(unMarshallMe)
def toMapOf[V]()(implicit m: Manifest[V]): Map[String,V] = JsonUtil.toMapOf[V](unMarshallMe)
def fromJson[T]()(implicit m: Manifest[T]): T = JsonUtil.fromJson[T](unMarshallMe)
}
implicit class Marshallable[T](marshallMe: T) {
def toJson: String = JsonUtil.toJson(marshallMe)
}
}
Se você importar as conversões implícitas, poderá chamar toJson em qualquer objeto e toMap ou fromJson em qualquer String.
import utils.MarshallableImplicits._
case class Person(name:String, age: Int)
val jeroen = Person("Jeroen", 26)
val jeroenJson = jeroen.toJson
// jeroenJson: String = {"name":"Jeroen","age":26}
val jeroenMap = jeroenJson.toMap
// jeroenMap: Map[String,Any] = Map(name -> Jeroen, age -> 26)
Boa codificação 🙂