Статьи

Scala: работа с JSON

Работа с JSON является очень распространенной задачей в процессе разработки программного обеспечения. В Scala вы можете сделать это разными способами, либо с помощью популярных библиотек Java, таких как Jackson, или с помощью специальных библиотек Scala. Как сделать правильный выбор из Spray JSON, Play JSON , Argonaut, Jackson, Rapture и многих других?

Однажды я получил тестовое задание на собеседовании. В соответствии с этим мне пришлось реализовать сериализацию и десериализацию JSON объекта Checkout в контексте электронной коммерции. Я проанализировал все существующие библиотеки JSON для Scala, я использовал каждую из них для небольших выборок и решил взять библиотеку Play JSON для этой задачи. Я сделал это, потому что у него есть исчерпывающая документация, много примеров, это часть самой популярной веб-платформы Scala, и мне нравится ее API.

Вопрос заключался в том, как использовать JSON-модуль PlayFramework без всего фреймворка. И я нашел отдельный репозиторий git с API Play JSON.

Для тех из вас, кто хочет увидеть, как использовать модуль Play JSON отдельно от Play Framework, я продолжу этот пост.

Как использовать модуль Play JSON?

Во-первых, вам нужно добавить библиотеку Play JSON в ваш проект. Вы можете сделать это различными способами — скачать код библиотеки и включить его в проект или использовать какой-либо инструмент управления зависимостями. Лично я использую SBT в проектах Scala.

Вот как мы можем добавить зависимость Play JSON в файл SBT:

1
2
3
4
5
6
7
name := "proectj-name"
 
version := "1.0"
 
scalaVersion := "2.11.7"
 
libraryDependencies ++= Seq("com.typesafe.play" % "play-json_2.11" % "2.4.2")

Тогда вы, вероятно, захотите посмотреть, как сериализация и десериализация JSON могут выполняться с помощью этой библиотеки. Что ж, давайте сделаем короткую демонстрацию. Вот модель JSON, и нам нужно работать с ней:

01
02
03
04
05
06
07
08
09
10
11
12
{
  "id": 1,
  "type": "credit card",
  "address": {
    "address1": "Baker str 3",
    "address2": "",
    "city": "London",
    "zipcode": "WC064"
  },
  "token": "u4lPaa74M"
  "cvv": 112
}

Это часть модели Checkout — оплата. Что нам нужно сделать сначала, чтобы реализовать сериализацию и десериализацию для этой модели JSON?

  1. Создайте модель Scala для самой глубокой модели в JSON. Вот класс случая для поля Address:
    1
    2
    3
    4
    5
    case class Address(address1: String,
                       address2: Option[String],
                       city: String,
                       state: String,
                       zipcode: String)
  2. Объявите правила записи и чтения , которые используются для записи модели Scala в JSON и для чтения JSON в Scala. Эта логика может быть объявлена ​​в объекте Address:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    object Address {
     
      import play.api.libs.json._
     
      implicit val addressFormats = Json.format[Address]
     
      def writeAddress(address: Address) = {
        Json.toJson(address)
      }
     
      def readAddress(jsonAddress: JsValue) = {
        jsonAddress.as[Address]
      }
     
    }

    Как видите, мы используем объект Play Json , чтобы выполнить сериализацию и десериализацию объекта с простыми полями. Под простыми я подразумеваю строки, числа, массивы и нулевые значения.

  3. Выполните действия № 1, № 2 для родительского объекта.
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    case class Payment(id: Long,
                       pType: String,
                       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)
      }
     
    }

    Мы получили более подробный код выше, это связано с именем поля типа . Поскольку слово типа зарезервировано в Scala, мы не можем использовать его как имя переменных в Scala. Таким образом, мы должны определить чтение и запись вручную для модели Payment.

Как проверить сериализацию и сериализацию в Scala?

Чтобы проверить, что все работает нормально, мы можем создать модульные тесты. Добавьте зависимость ScalaTest в файл SBT. Теперь это должно выглядеть так:

1
2
3
4
5
6
7
8
9
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")

И напишите тесты для оплаты:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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")
  }
 
}

Резюме

Модуль JSON Play действительно мощный. Я не описал много вещей, которые можно сделать с его помощью. Но я рекомендую вам прочитать официальную документацию и посмотреть другие примеры. Обратите особое внимание на версии документов, которые вы читаете, и Scala, которые вы используете в проекте.

Ссылка: Scala: работа с JSON от нашего партнера JCG Алексея Зволинского в блоге заметок Фрузенштейна .