1. JsValue可以理解为是Json对象(更准确的其实JsObject才是Json对象, JsValue更像是Json值的表示)
  2. Ok是Play框架中的响应返回,表示正确响应,可传参数作为响应信息,如Ok(Json.obj(“Ok” -> “OK”))
  3. 隐式参数和对象不需要手动传入,只要它们在当前上下文是可见的即可自动引用,一般在伴生对象或包对象中。
  4. Scala无真正的基本类型,这里说的基本是数值,数值的数组,数值的集合(集合元素也必须数值的)
  5. 对于不确定的情况需要尽量使用带Opt结尾的方法,否则对None值调用get会报错。
  6. Scala把所有Java基本类型都映射成了Scala对象,Scala中一切都是对象,都能进行函数调用,其中有的比如String、Class等等是直接使用了Java的String、Class

下面对于给定数据进行说明

 val queryParamsRequest: Map[String, Seq[String]] = Map("clickId" -> Seq("1", "2", "3", "4"), "currTime" -> Seq("5", "4", "3", "2"))

toJson可以对简单类型数据直接序列化,而不需任何其他操作

 val jsValue = Json.toJson(queryParamsRequest)

得到的是JsValue

查看toJson源码,如下

 def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue = tjs.writes(o)

该方法使用隐式参数Writes来完成写入。实际上这里是因为Scala默认定义了一些用于基础类型的Writes

trait DefaultWrites extends LowPriorityWrites {
import scala.language.implicitConversions

/**
 * Serializer for Int types.
 */
implicit object IntWrites extends Writes[Int] {
  def writes(o: Int) = JsNumber(o)
  }
......
implicit object DoubleWrites extends Writes[Double] {
  def writes(o: Double) = JsNumber(o)
}
...... 此处省略还有数组等

其中JsNumberJsValue的子类

case class JsNumber(value: BigDecimal) extends JsValue

也就是默认所有的基本类型即使不传入任何参数也是可以使用toJson方法将其转换为JsValue。反之,如果有不是基本类型的,则需要自己提供隐式的Writes,假设有class如下

case class AppConfig(title: String, baseUri: URI)

URI是java.net.URI类 那现在肯定无法自动序列化为json,此时可以自定义一个隐式参数如下

implicit val appConfigWrites = new Writes[AppConfig] {
      def writes(appConfig: AppConfig) = Json.obj(
          "title" -> appConfig.title,
          "baseUri" -> appConfig.baseUri.toString 
          //对于不能转换为String的这里要增加 额外处理
      )
  }

PS:如果想从给定的json字符串中读取出AppConfig对象则需要Reads,这是反序列化,可能会出错,一般使用Option

implicit val appConfigReads: Reads[AppConfig] = (
    (JsPath \ "title").read[String] and
      (JsPath \ "baseUri").read[String].map(x => new URI((x)))
    ) (AppConfig.apply _)

你也可以同时定义序列化和反序列化,有了Writes对象或Format对象实现了writes方法,就可以在网络中将DTO对象进行传输。(以Json字符串传输)

new Format[AppConfig] {
    override def writes(o: AppConfig): JsValue = ???
//实现这两个方法。代码和上面的类似。如果是DTO对象,一般不实现Reads,可以在
//被调用时就抛出异常
    override def reads(json: JsValue): JsResult[AppConfig] = ???
}

如果想直接写入Play的Ok()中,还需要增加一个Writeable隐式对象,如下

implicit def jsonWritable[A](implicit writes: Writes[A], codec: play.api.mvc.Codec): Writeable[A] = {
    implicit val contentType = ContentTypeOf[A](Some(ContentTypes.JSON))
    val transform = Writeable.writeableOf_JsValue.transform compose writes.writes
    Writeable(transform)
  }

现在接着上面序列化Map,得到JsValue后可以转换为String

val jsonQueryParams = Json.stringify(jsValue)
println("序列化成字符串 => " + jsonQueryParams)

如果你想字符串是易于观看的,可以选择格式化展现,如下

val prettyPrint = Json.prettyPrint(jsValue)
println("JsValue格式化成字符串 => " + prettyPrint)

这里格式化使用的是JacksonJson 如果想从给定的json字符串反序列成JsValue,可以执行解析操作,如下

val json = Json.parse(jsonQueryParams)
println("反序列的Json值 => " + json)

这里从字符串解析使用的也是JacksonJson,最后想将字符串解析成Map

val queryParamResult = json.validate[Map[String, Seq[String]]].getOrElse(Map())

不是确定类型的情况下最好使用 validateOpt 如果想打印map的key和value可以使用for

for ((k, v) <- queryParamResult) {
   println(k, v)
}

上面序列化和反序列化Map的结果

序列化成字符串 => {"clickId":["1","2","3","4"],"currTime":["5","4","3","2"]}
JsValue格式化成字符串 => {
  "clickId" : [ "1", "2", "3", "4" ],
  "currTime" : [ "5", "4", "3", "2" ]
}
反序列的Json值 => {"clickId":["1","2","3","4"],"currTime":["5","4","3","2"]}