Scalajson - opensas/Play20Es GitHub Wiki
Esta página todavía no ha sido traducida al castellano. Puedes ayudarnos con la tarea simplemente presionando el botón
Edit Page. Para más información puedes leer esta guía para el traductor. Aquí puedes ver cuánto nos falta para terminar la traducción.
La manera más aconsejable de trabajar con JSON es utilizando la librería JSON basada en los tipos de datos de las clases, que puede encontrar en play.api.libs.json. Esta librería está basada en Jerkson, la cual es a su vez un wrapper para Scala de la super rápida Jackson para manejo de JSON en Java.
El beneficio de este enfoque es que tanto la versión Java como Scala de Play comparten la mimsa librería de base (Jackson), al mismo tiempo que los usuarios de Scala podrán aprovechar la seguridad de tipos extra que Play le brinda al trabajar con JSON.
Cómo serializar (marchal) y deserializar (parse) información JSON hacia nuestros objetos de dominio.
El paquete play.api.libs.json contiene siete tipos de datos de JSON:
JsObjectJsNullJsUndefinedJsBooleanJsNumberJsArrayJsString
Todos ellos heredan del valor JSON genérico JsValue.
Using these one can build a type safe JSON deserializer and encapsulate the whole logic like this. For example:
case class User(id: Long, name: String, friends: List[User])
implicit object UserFormat extends Format[User] {
def reads(json: JsValue): User = User(
(json \ "id").as[Long],
(json \ "name").as[String],
(json \ "friends").asOpt[List[User]].getOrElse(List())
)
//unmarshaling to JSValue is covered in the next paragraph
def writes(u: User): JsValue = JsObject(Seq(
"id" -> JsNumber(u.id),
"name" -> JsString(u.name),
"friends" -> JsArray(u.friends.map(toJson(_)))
))
}Note:
Formatdefines two methods:readsandwrites, which are responsible for marshalling to and fromJsValue.
Given this, one can marshall an incoming JSON string into a User case class like this:
val data = play.api.libs.json.Json.parse(incomingJSONstring).as[User]Alternatively, if the data is coming from a request.body:
val user = request.body.asJson.map(_.as[User]).getOrElse(
throw new RuntimeException("could not create user")
)It’s also possible to pattern match on JsValue in cases where the underlying JSON type is not homogenous (say a JSON Map with different JSON types) or if one wants to manage object creation.
import play.api.libs.json._
import Json._
//{"newspaper":{"url":"http://nytimes.com","attributes":{"name":"nytimes","country":"US","id":25,"links":["http://link1","http://link2"]}}}
val jsonMap = com.codahale.jerkson.Json.generate(Map(
"newspaper" -> Map(
"url" -> "http://nytimes.com",
"attributes" -> Map("name" -> "nytimes", "country" -> "US", "id" -> 25, "links" -> List("http://link1", "http://link2"))
)))
val data = parse(jsonMap)
case class Attributes(name: String, id: Int, links: List[String])
val attributes = (data \ "attributes")
println(Attributes( (attributes \ "name") match {case JsString(name)=>name;case _ => ""},
(attributes \ "id") match {case JsNumber(id)=>id.toInt;case _ => 0},
(attributes \ "links") match {case JsArray(links)=>links.map(_.as[String]);case _ => Nil}))
Note:
\\means look-up in the current object and all descendants,\means only look-up the corresponding property.
Parsing JSON is just half of the story, since in most situations we would like to return JSON as well. Let’s revisit the previous example:
import play.api.libs.json._
case class User(id: Long, name: String, friends: List[User])
implicit object UserFormat extends Format[User] {
def reads(json: JsValue): User = User(
(json \ "id").as[Long],
(json \ "name").as[String],
(json \ "friends").asOpt[List[User]].getOrElse(List()))
def writes(u: User): JsValue = JsObject(List(
"id" -> JsNumber(u.id),
"name" -> JsString(u.name),
"friends" -> JsArray(u.friends.map(fr => JsObject(List("id" -> JsNumber(fr.id), "name" -> JsString(fr.name)))))))
}Note: The main building block is
JsObject, which takes aSeq[String,JsValue].
Then:
import play.api.libs.json._
import play.api._
import play.api.mvc._
object MyController extends Controller{
def sendJson(id: String) = Action {
val peter = User(id.toLong, "peter", friends: List())
Ok(toJson(peter))
}
}Alternatively, one can build an unnamed JsObject structure and return it:
import play.api.libs.json._
import play.api._
import play.api.mvc._
object MyController extends Controller{
def sendJson(id: String) = Action {
Ok(toJson(JsObject(List("key"->JsString("cool"), "links"->JsObject(List("name"->JsString("foo"), "links" ->JsArray(List(JsNumber(25)))))))))
}
}Finally, it’s also possible to unmarshall standard and typed data structures directly:
import play.api.libs.json._
import play.api._
import play.api.mvc._
object MyController extends Controller{
def sendJson(id: String) = Action {
//this won't work, since it's not properly typed
//toJson(Map("f"->Map[String,Any]("s"->List("1","f"),"f"->"f" )))
//this will return JSON as a String
//Json.stringify(toJson(Map("f"->Map("s"->List("1","f")))))
//this will return a JSON string with content type "application/json"
Ok(toJson(Map("f"->Map("s"->List("1","f")))))
}
}The benefit of the type class-based solution is that it significantly increases type safety, albeit at the cost of maintaining the extra mapping or type conversion to JsValue that is necessary to unmarshall data from domain objects to JSON.
While the type class-based solution described above is the one that’s recommended, there is nothing stopping you from using other JSON libraries.
For example, here is a small snippet that demonstrates how to marshall a plain Scala object into JSON and send it over the wire using the bundled, reflection-based Jerkson library:
object MyController extends Controller{
import com.codahale.jerkson.Json._
def sendJson(id: String) = Action {
val dataFromDataStore = Map("url"->"http://nytimes.com","attributes"-> Map("name"->"nytimes", "country"->"US","id"->25), "links"->List("http://link1","http://link2")
Ok(generate(dataFromDataStore)).as("application/json")
}
}