【问题标题】:Mapping Seq to JSON in Play在 Play 中将 Seq 映射到 JSON
【发布时间】:2014-08-21 07:59:42
【问题描述】:

我正在尝试使用 Play 2.x 将 Scala 案例类映射到 JSON。这适用于案例类的简单版本,但不适用于涉及 Seq 或对象列表时:然后我得到“没有隐式格式”和“没有找到未应用函数”错误。

我使用的代码如下:

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

我已使用Json.format 宏为此生成读取和写入:

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]] 添加到隐式转换器,但没有效果。

【问题讨论】:

  • 以相反的顺序定义它们应该可以工作:implicit val authorFormat = Json.format[Author] implicit val bookFormat = Json.format[Book]
  • 哇,我错过了。谢谢!顺便说一句,你还有一个建议不要扩大这个列表吗?我真的需要为每个案例类手动注册一个 Json.format 吗?

标签: json scala playframework


【解决方案1】:

为图中需要序列化的每个类定义尊重其依赖顺序的格式化程序。

格式化Book需要格式化Author,所以在Book格式化之前定义Author格式化。

例如,使用此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

应该记住,要序列化的类不一定是领域模型类。声明反映所需 JSON 结构的数据传输对象 (DTO) 类并从域模型中实例化它们可能会有所帮助。这样一来,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))
  }

}

【讨论】:

  • 感谢您的详尽解释和出色的代码示例。这很有帮助。但是我仍然必须为每个班级注册一个格式化程序?为每个案例类执行此操作似乎有点冗长?我在您提供的链接中没有看到它。
  • 是的,如答案中所述,有必要为每个需要序列化的类定义格式化程序(或编写器)。当使用 DTO 模式时,这通常不是必需的,因为 DTO 可以根据不需要特定序列化程序的简单类型来定义。请参阅我添加到答案中的该模式示例。
  • 导入 models.Formatters._ 完美!你救了我的一天谢谢老板(:
猜你喜欢
  • 2019-01-05
  • 1970-01-01
  • 2019-05-27
  • 2014-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-16
  • 2018-01-19
相关资源
最近更新 更多