Simple Play2: Play Json

How to use Play JSON module?

首先添加JSON依赖:

libraryDependencies ++= Seq("com.typesafe.play" % "play-json_2.11" % "2.4.2")

然后这是我们需要处理的JSON用例:

{
  "id": 1,
  "type": "credit card",
  "address": {
    "address1": "Baker str 3",
    "address2": "",
    "city": "London",
    "zipcode": "WC064"
  },
  "token": "u4lPaa74M"
  "cvv": 112
}

这是一个支付账单,有两层嵌套,我们如何处理呢?

  1. 首先创建JSON中最深层次的模型,这是address的模型:

    case class Address(address1: String,
                       address2: Option[String],
                       city: String,
                       state: String,
                       zipcode: String)
    
  2. 声明读(reads)写(writes)规则,用于在Scala模型和JSON之间互相转换,这个逻辑可以在Address的伴生对象中创建:

    object Address {
    
      import play.api.libs.json._
    
      implicit val addressFormats = Json.format[Address]        // Json对象
    
      def writeAddress(address: Address) = {
        Json.toJson(address)
      }
    
      def readAddress(jsonAddress: JsValue) = {
        jsonAddress.as[Address]
      }
    
    }
    

    可以看到我们使用了Play中的Json对象,提供了对简单类型的序列化和反序列化转换,比如字符串,数字,列表等.

  3. 完成json实例中的主题部分,即Payment:

    case class Payment(id: Long,
                       pType: String,           // type在Scala中是关键字,进行了修改
                       address: Address,        // 使用Address作为嵌套类型
                       token: String,
                       cvv: String)
    
    object Payment {
    
      import play.api.libs.json._
    
      def writePayment(payment: Payment) = {
        JsObject(Seq(
          "id" -> JsNumber(payment.id),
          "type" -> JsString(payment.pType),
          "address" -> Json.toJson(payment.address),
          "token" -> JsString(payment.token),
          "cvv" -> JsString(payment.cvv)
        ))
      }
    
      def readPayment(jsonPayment: JsValue) = {
        val id = (jsonPayment \ "id").as[Long]
        val pType = (jsonPayment \ "type").as[String]
        val address = (jsonPayment \ "address").as[Address]
        val token = (jsonPayment \ "token").as[String]
        val cvv = (jsonPayment \ "cvv").as[String]
        Payment(id, pType, address, token, cvv)
      }
    }
    

    由于Payment中使用了嵌套类型Address,这时就不能通过使用 Json.format[Payment] 来完成转换,因此通过定义其中的每个字段的转换方法,来实现读写的支持.

How to test serialisation & serialisation in Scala?

添加测试框架依赖:

name := "proectj-name"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies ++= Seq(
  "org.scalatest" % "scalatest_2.11" % "3.0.0-SNAP5" % "test",
  "com.typesafe.play" % "play-json_2.11" % "2.4.2")

然后编写测试用例:

import models._
import models.Payment._

import org.scalatest._
import play.api.libs.json._

class PaymentTest extends FlatSpec with Matchers {

  val address = Address("1375 Burlingame Ave.", None, "Burlingame", "California", "94010")

  "Payment " should "be converted to JSON correctly " in {      // 序列化测试

    val payment = Payment(1, "creditCard", address, "wdweadowei3209423", "123")
    val paymentJSON = writePayment(payment)

    (paymentJSON \ ("id")).get should be (JsNumber(1))
    (paymentJSON \ ("type")).get should be (JsString("creditCard"))
    (paymentJSON \ ("address")).get should be (Json.toJson(payment.address))
    (paymentJSON \ ("token")).get should be (JsString("wdweadowei3209423"))
    (paymentJSON \ ("cvv")).get should be (JsString("123"))

  }

  it should " be deserialized correctly " in {                  // 反序列化测试
    val paymentJSON: JsValue = JsObject(Seq(
      "id" -> JsNumber(1),
      "type" -> JsString("creditCard"),
      "address" -> Json.toJson(address),
      "token" -> JsString("wdweadowei3209423"),
      "cvv" -> JsString("123")
    ))

    val payment = readPayment(paymentJSON)

    payment.id should be (1)
    payment.pType should be ("creditCard")
    payment.address should be (address)
    payment.token should be ("wdweadowei3209423")
    payment.cvv should be ("123")
  }

}

更多细节可以参考官方文档.

…..