Note
This post is a continuation of this post, which is the thrid part of a blog suite that aims the use of Neo4j and Play2.0 together on Heroku.Play2.0 - Scala - Json
I'm about to write a quick wrap up, of some Play20's wiki entries and stackoverflow that were all related to Json in Play2.0.For that, I'll take some usage examples, but also present the underlying libraries (Jerkson) and the used paradigm, SJson.
Scope
Giving that the wiki pages are really clean and self-explaining, I'm not gonna enter deeply in how Json must be used with Play 2.0 albeit I'll give some prerequesites in order to help you understand how I'll use the Neo4J REST API.play.api.libs.json
This package contains everything you'll need in order to work with Json in Play 2.0.It defines important structure like JsObject, JsArray and even some like JsUndefined.
They usage is very easy since they are based on classical scala's Map and List of JsValue(parent type).
Here is an example of creating a JsArray and iterating items. To test it in a REPL, I recommend you to enter the Play console by using play in your repo and the console in sbt (to have all libraries loaded).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val ar:JsArray = JsArray(List(JsString("a"), JsString("b"), JsString("c"))) | |
//... or | |
// val ar:JsArray = JsArray(List("a","b","c").map(JsString(_))) | |
val listOfStrings = ar.as[List[JsValue]] // this uses the Format (Reads) that I'll talk brievly further | |
//... or simply | |
// val listOfStrings = ar.value | |
//show items | |
listOfStrings.map { case a:JsString => println(a.value) } | |
//using Format | |
listOfStrings.map { println(_.as[String]) } |
For arrays, it's quite easy (maybe wrapping could be annoying but a little of pimping can resolve that).
The Jerkson library powerful comes with JsObject usages. It has defined a very clean DSL for querying Json. Here is an example for querying a property or catch a descendant property.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val ar:JsArray = JsArray(List(JsString("a"), JsString("b"), JsString("c"))) | |
//... or | |
// val ar:JsArray = JsArray(List("a","b","c").map(JsString(_))) | |
val listOfStrings = ar.as[List[JsValue]] // this uses the Format (Reads) that I'll talk brievly further | |
//... or simply | |
// val listOfStrings = ar.value | |
//show items | |
listOfStrings.map { case a:JsString => println(a.value) } | |
//using Format | |
listOfStrings.map { println(_.as[String]) } |
play.api.libs.json.{Format, Reads, Writes}
Until now, you saw that Json is usable with Play. But, I guess that you hope more than that, since this framework is here to ease the work.And you're right.
Play is coming with a SJson flavor for serialization and deserialization of DSO.
Three traits come in the game.
Reads
Reads defines a simple method reads:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Reads[T] { | |
/** | |
* Convert the JsValue into a T | |
*/ | |
def reads(json: JsValue): T | |
} |
With the object Reads that defines an implicit Reads instance for most common types like Option, String, Short ...
Writes
Writes, like Reads, is very simple and defines a simple method writes:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Writes[T] { | |
/** | |
* Convert the object into a JsValue | |
*/ | |
def writes(o: T): JsValue | |
} |
A Writes object is defined too with conversion to common types.
Format
This trait is there to put the pieces together
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Format[T] extends Writes[T] with Reads[T] | |
/** | |
* Default Json formatters. | |
*/ | |
object Format extends DefaultFormat |
A good practice is to define such Format into the companion object of your DSO, so that it will come in the scope at once when using it.
Here is an example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DSO(val prop:String) {} | |
object DSO { | |
implicit object DSOFormat extends Format[DSO] { | |
def reads(json: JsValue): DSO = new DSO( | |
(json \ "prop").asOpt[String].getOrElse("Not Defined") | |
) | |
def writes(d: DSO): JsValue = | |
JsObject(List( | |
"prop" -> JsString(d.prop) | |
)) | |
} | |
} |
play.api.libs.json.Json._
This object is an handy one, that defines four convenient methods. Two are here to play with String and JsValue. The other are to play with types and JsValue.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def parse(input: String): JsValue = JerksonJson.parse[JsValue](input) | |
def stringify(json: JsValue): String = JerksonJson.generate(json) | |
// and | |
def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue = tjs.writes(o) | |
def fromJson[T](json: JsValue)(implicit fjs: Reads[T]): T = fjs.reads(json) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
scala> val d = new DSO("a prop") | |
d: DSO = DSO@184a5d6 | |
scala> d.prop | |
res1: String = a prop | |
scala> toJson(d) | |
res2: play.api.libs.json.JsValue = {"prop":"a prop"} | |
scala> fromJson[DSO](res2).prop | |
res3: String = a prop</pre> |
Enhancement
In order to have more control on effects of your serialization, I would recommend you to consider the Scalaz library's Validation construct.Indeed, it will help you having more relevant information and all at once if you reads is wrong.
Here is a talk about this (but not in Play).
In the next post, we'll talk about the Dispatch library. See it here
No comments:
Post a Comment