首页 文章

在Play中将Seq映射到JSON

提问于
浏览
14

我正在尝试使用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]] 添加到隐式转换器,但这没有任何效果 .

1 回答

  • 10

    为需要序列化的图中的每个类定义遵循其依赖顺序的格式化程序 .

    格式化 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))
      }
    
    }
    

相关问题