内容简介:Play 内置了一套JSON库,以帮助开发者简化JSON操作。目前Play的JSON库包含以下功能:Play的JSON库并不依赖于Play环境,可以单独使用,通过如下方式可以将它引入到自己的项目:所有的基本JSON类型都继承自
Play Json 简介
Play 内置了一套JSON库,以帮助开发者简化JSON操作。目前Play的JSON库包含以下功能:
-
Json对象与字符串之间互转
-
Json对象和Case Class之间互转
-
Json数据校验
-
Json格式之间互转
Play的JSON库并不依赖于Play环境,可以单独使用,通过如下方式可以将它引入到自己的项目:
libraryDependencies += "com.typesafe.play" %% "play-json" % playVersion
基本JSON类型
所有的基本JSON类型都继承自 JsValue
trait。Play JSON 库提供的基本类型如下:
-
JsString
-
JsNumber
-
JsBoolean
-
JsObject
-
JsArray
-
JsNull
在日程开发中,我们很少跟这些JSON基本类型打交道。因为在Play中对于基本类型T(例如 String, Int, ...)以及Seq[T]已经提供了默认的隐式转换, 可以自动将其转换成对应的JSON类型,例如:
//基本类型值 Json.obj("name" -> JsString("joymufeng")) //可以简写成: Json.obj("name" -> "joymufeng") //序列类型值 Json.obj("emails" -> JsArray(Seq(JsString("a"), JsString("b")))) //可以简写成: Json.obj("emails" -> Seq("a", "b"))
在Play的JSON库里,整形和浮点型都使用JsNumber表示,这是一个略为糟糕的设计,因为会导致JSON数据无法在多语言环境下共享。例如通过 Java 代码向 MongoDB 写入了一个整形数值,但是经过Play的JSON库修改后变成了浮点型,Java代码再次读取时便会报错。
基本的JSON操作
构建一个JsObject对象
//直接构建 val json = Json.obj( "name" -> "joymufeng", "emails" -> Json.arr("joymufeng@163.com"), "address" -> Json.obj( "province" -> "JiangSu", "city" -> "NanJing" ) ) //从JSON字符串构建 val json = Json.parse("""{ "name": "joymufeng", "emails": ["joymufeng@163.com"], "address": { "province": "JiangSu", "city" -> "NanJing" } }""")
读写操作
//读取指定路径值 val city = (json \ "address" \ "city").as[String] //如果指定路径不存在则返回None val cityOpt = (json \ "address" \ "city").asOpt[String] //读取数组内容 val emails = (json \ "emails").as[List[String]] //读取数组的第1个元素 val email = (json \ "emails")(0) //更新指定路径值 var obj = Json.obj("a" -> 1) obj ++= Json.obj("b" -> 2) //obj: {"a":1,"b":2}
格式化输出
//格式化输出 val prettyStr = Json.prettyPrint(obj)
JsObject 与 Case Class 互转
Json Format 宏
Play虽然为基本类型T以及Seq[T]提供了默认的隐式转换,但是对于用户自定义的 Case Class,由于无法事先知晓,需要需要用户自己声明隐式转换对象。Play 为开发者提供了 Format 宏,只需要一行代码便可以完成声明操作。假设我们有如下两个 Case Class:
case class Address(province: String, city: String) case class Person(name: String, emails: List[String], address: Address)
我们只需要声明如下两个隐式的Format对象就可以了,在运行时,隐式的 Format 对象会自动完成编解码操作:
import play.api.libs.json.Json implicit val addressFormat = Json.format[Address] implicit val personFormat = Json.format[Person]
Format 宏的展开是在编译期执行的,一方面提升了类型的安全性,另一方,区别于 Java 的反射机制,Format 宏是在编译器生成编解码器,在运行期有更高的执行效率。关于 Scala 宏的更多内容请参考 官方文档 。
常见互转操作
将上面两个隐式 Format 对象导入到当前作用域,我们便可以自由地在 JsObject 和 Case Class 之间进行互转:
val person = Person("joymufeng", List("joymufeng@163.com"), Address("JiangSu", "NanJing")) //将 Case Class 转换成 Json val json = Json.toJson[Person](person) //将 Json 转换成 Case Class val p1 = Json.fromJson[Person](json).get //或者 val p2 = json.as[Person] val p3 = json.asOpt[Person].get
我们发现 Json.fromJson[Person](json)
返回的类型并不是 Person
而是 JsResult[Person]
,这是因为从 Json 到Case Class的转换可能会发生错误, JsResult
有两个子类 JsSuccess
和 JsError
,分别用来处理成功和失败两种情况:
Json.fromJson[Person](json) match { //转换成功 case p: JsSuccess[Person] => println(p) //转换失败 case e: JsError => println(e.errors) }
开发技巧
上面的代码在转换时需要将隐式的 Format 对象显式地导入到当前的作用域,使用起来有些不便。我们可以把隐式 Format 对象定义在伴生对象中,这样的话就可以在任意位置执行转换而无需导入隐式对象:
import play.api.libs.json.Json case class Address(province: String, city: String) case object Address { implicit val addressFormat = Json.format[Address] } case class Person(name: String, emails: List[String], address: Address) case object Person { implicit val personFormat = Json.format[Person] }
编译器会自动到目标类型和源类型的伴生对象中获取隐式的 Format 对象,无需手动导入。
上面的方法需要针对每个 Case Class 创建一个伴生对象,编写起来比较繁琐。我们也可以在包对象(package object)中创建隐式的 Format 对象,假设 Address 和 Person 都定义在 models 包下,则我们可以为 models 包创建一个包对象,并在其中创建隐式的 Format 对象:
package object models { import play.api.libs.json.Json implicit val addressFormat = Json.format[Address] implicit val personFormat = Json.format[Person] }
由于编译器会自动从源类型或目标类型的父对象中查找隐式转换,所以定义在包对象中的隐式 Format 对象会被自动加载,而无需显示导入。
更多的隐式转换来源请参考官方的总结的 隐式转换规则 。
Json 请求与 Json 响应
Json
是目前使用最为广泛的数据交换格式,利用 Play 的 Json 库,我们可以开发非常健壮的 RESTful 应用。
构建 Json 请求
借助 jQuery
可以很容易构建一个请求体为 Json
的 Post
请求:
$.ajax({ type: 'post', dataType: 'json', contentType: 'application/json; charset=utf-8', data: {...}, url: '/post', success: function(res){ //请求成功处理 }, error: function(e){ //请求失败处理 } });
需要注意,客户端在执行 Post
请求时必须明确指定 Content-Type
请求头,否则服务器端无法正确识别。
处理 Json 请求
在服务器端,我们可以通过如下方式接收 Json 请求:
def doReciveJson = Action { implicit request => request.body.asJson match { case Some(obj) => obj.validate[Person] match { case JsSuccess(person, _) => Ok(Json.obj("status" -> 0)) case JsError(_) => Ok(Json.obj("status" -> 1, "msg" -> "Json数据校验失败!")) } case None => Ok(Json.obj("status" -> 1, "msg" -> "请求格式有误!")) } }
再次提醒,客户端 Post 请求必须携带 Content-Type
请求头,否则服务器端在执行 request.body.asJson
代码时将无法正确解析出 Json 数据。通过 request.body.as*
方法,我们可以将请求体转换成不同的数据格式,前提是请求的 Content-Type
内容必须与目标数据格式一致。下面列举常见的as*方法所要求的Content-Type类型,供大家开发时参阅:
-
asJson
:text/json 或 application/json -
asText
:text/plain -
asFormUrlEncoded
:application/x-www-form-urlencoded -
asXml
:application/xml -
asMultipartFormData
:multipart/form-data -
asRaw
:其它类型
在服务器端,我们可以构建一个 Json 对象,并且直接作为响应写回客户端,Play 会自动添加合适的响应头:
Ok(Json.obj("status" -> 0))
在生成 Json 响应时,我们并没有明确指定字符编码格式,这是由于按照 RFC 7159 规范,Play 使用默认的 UTF-8 对 Json 内容进行编码,客户端可以通过检测 Json 内容的前4个字节自动检测出 UTF-8 字符编码,继而可以正确解码 Json 内容。
RFC 7159 规定在为 Json 指定 Content-Type 时无需指定编码格式,并且指定编码格式是非法操作。客户端可以根据 Json 内容的前4个字节自动检测出正确的编码格式。
小结
随着NoSQL数据库和微服务的不断普及,JSON数据在Web开发中显得越来越重要。借助 MongoDB 等 BSON数据库,我们可以实现全栈式 Json 开发,大大简化了数据的处理流程。例如对于复杂的业务数据,如绘图 工具 导出的 Json 数据,我们可以直接入库,省去中间的 Case Class 相互转换过程。在 Json 处理领域,Play 和 Scala 有着天然的优势,一方面通过 Scala 的优雅语法以及 Play 的 Json DSL,我们可以轻松地构建和处理 Json;另一方面,相比于 Java 的反射机制,利用 Scala 语言提供的编译器期 Macro,可以大大提升运行时处理速度,为开发高性能的响应式系统提供了底层的技术保障。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- linux 内核开发指南 - 2 开发流程
- 面向Java开发人员的Flex开发指南
- (是时候开发属于自己的插件了)数据校验插件开发指南
- 原 荐 Play For Scala 开发指南 - 第10章 MongoDB 开发
- NGINX 开发指南
- 网站图标开发指南
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Introduction to Computation and Programming Using Python
John V. Guttag / The MIT Press / 2013-7 / USD 25.00
This book introduces students with little or no prior programming experience to the art of computational problem solving using Python and various Python libraries, including PyLab. It provides student......一起来看看 《Introduction to Computation and Programming Using Python》 这本书的介绍吧!