JSON (des) marshalling fácil em Scala com Jackson

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 🙂