Подтвердить что ты не робот

Сопоставление Seq с JSON в игре

Я пытаюсь сопоставить класс case Scala с JSON, используя Play 2.x. Это работает для простых версий класса case, но не там, где есть Seq или List объектов: тогда я получаю ошибки "неявный формат" и "отсутствие ошибок при использовании unapply".

Код, который я использую для этого, следующий:

case class Book(title: String, authors: Seq[Author])
case class Author(name: String)

Я использовал макрос Json.format для создания Reads и Writes для этого:

implicit val bookFormat = Json.format[Book]
implicit val authorFormat = Json.format[Author]

Но теперь, когда я компилирую свой код, я получаю следующую ошибку:

Error:(25, 40) Play 2 Compiler: 
 /Users/erikp/Userfiles/projects/play/booksearch/app/models/user.scala:25: No implicit format for Seq[models.Author] available.
   implicit val bookFormat = Json.format[Book]
                                        ^

Без Seq он работает красиво, но с Seq он терпит неудачу. Я попытался добавить implicit val authorsFormat = Json.format[Seq[Author]] к неявным преобразователям, но это не имеет никакого эффекта.

4b9b3361

Ответ 1

Определите форматы, соответствующие их порядку зависимости, для каждого класса в графе, который должен быть сериализован.

Форматирование Book требует форматирования Author, поэтому определите формат Author перед форматом Book.

Например, с этим Models.scala файлом:

package models

import play.api.libs.json._

case class Book(title: String, authors: Seq[Author])
case class Author(name: String)

object Formatters {
  implicit val authorFormat = Json.format[Author]
  implicit val bookFormat = Json.format[Book]
}

и этот JsonExample.scala файл:

package controllers

import models._
import models.Formatters._
import play.api.mvc._
import play.api.libs.json._

object JsonExample extends Controller {

  def listBooks = Action {
    val books = Seq(
      Book("Book One", Seq(Author("Author One"))),
      Book("Book Two", Seq(Author("Author One"), Author("Author Two")))
    )
    val json = Json.toJson(books)
    Ok(json)
  }

}

запрос listBooks приведет к такому результату:

< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Content-Length: 133
<     
[{"title":"Book One","authors":[{"name":"Author One"}]},{"title":"Book Two","authors":[{"name":"Author One"},{"name":"Author Two"}]}]

Для более продвинутого форматирования, включая частичную сериализацию, чтобы избежать объявления форматов для классов, которые не должны быть сериализованы, см. JSON Reads/Writes/Format Combinators.

Следует иметь в виду, что классы, которые должны быть сериализованы, необязательно должны быть классами модели домена. Может быть полезно объявить классы объектов передачи данных (DTO), которые отражают желаемую структуру JSON, и создать их экземпляр из модели домена. Таким образом, сериализация является простой с Json.format, и нет частичной сериализации с дополнительным преимуществом типичного представления JSON API.

Например, этот BookDTO.scala файл определяет объект передачи данных BookDTO, который использует только типы, которые могут быть сериализованы в JSON без необходимости дальнейшего определения:

package dtos

import models._
import play.api.libs.json.Json

case class BookDTO (title: String, authors: Seq[String])

object BookDTO {

  def fromBook(b: Book) = BookDTO(b.title, b.authors.map(_.name))

  implicit val bookDTOFormat = Json.format[BookDTO]

}

и этот файл JsonExample2.scala показывает, как использовать этот шаблон:

package controllers

import dtos._
import dtos.BookDTO._
import models._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.functional.syntax._

object JsonExample2 extends Controller {

  def listBooks = Action {
    val books = Seq(
      Book("Book One", Seq(Author("Author One"))),
      Book("Book Two", Seq(Author("Author One"), Author("Author Two")))
    )
    val booksDTO = books.map(BookDTO.fromBook(_))
    Ok(Json.toJson(booksDTO))
  }

}