2014-06-30 18 views
13

Ich versuche, eine JSON-Scala-Fallklasse mit Play 2.x zuzuordnen. Dies funktioniert für einfache Versionen der Fallklasse, aber nicht, wenn es eine Seq oder Liste von Objekten gibt: dann bekomme ich 'kein implizites Format' und 'keine unapplication function found' Fehler.Zuordnung von Seq zu JSON in Play

Der Code, den ich für diese Verwendung ist die folgende:

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

ich das Json.format Makro verwendet habe, das Lesen und Schreiben für diese zu generieren:

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

Aber jetzt, wenn ich Ich erhalte den folgenden Fehler:

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] 
             ^

Ohne Seq funktioniert es gut, aber wi th die Seq, es scheitert. Ich habe versucht, implicit val authorsFormat = Json.format[Seq[Author]] zu den impliziten Konvertern hinzuzufügen, aber das hat keine Auswirkung.

+3

sie in umgekehrter Reihenfolge definieren funktionieren sollte: implizite val authorformat = Json.format [Autor] implizite val bookFormat = Json.fo rmat [Buch] –

+0

Wow, das habe ich vermisst. Vielen Dank! Übrigens, haben Sie auch einen Vorschlag, diese Liste nicht weiter zu erweitern? Muss ich wirklich ein Json.format für jede Fallklasse manuell registrieren? –

Antwort

9

Definieren Sie die Formatierer unter Berücksichtigung ihrer Abhängigkeitsreihenfolge für jede Klasse im Diagramm, die serialisiert werden muss.

Formatierung Book erfordert Formatierung Author, so definieren Sie den Author Formatierer vor dem Book Formatierer.

Zum Beispiel mit dieser Models.scala Datei:

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] 
} 

und diese JsonExample.scala Datei:

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) 
    } 

} 

eine Anfrage an listBooks wird dieses Ergebnis produzieren:

< 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"}]}] 

Für fortgeschrittenere Formatierung, einschließlich partieller Serialisierung zu Avo ID muss Formatierer für Klassen deklarieren, die nicht serialisiert werden sollten, siehe JSON Reads/Writes/Format Combinators.

Es sollte berücksichtigt werden, dass die zu serialisierenden Klassen nicht notwendigerweise die Domänenmodellklassen sein müssen. Es kann hilfreich sein, Datenübertragungsobjektklassen (DTO-Klassen) zu deklarieren, die die gewünschte JSON-Struktur widerspiegeln, und diese aus dem Domänenmodell zu instanziieren. Auf diese Weise ist Serialisierung mit Json.format unkompliziert und es gibt nicht das Problem der partiellen Serialisierung, mit dem zusätzlichen Vorteil einer typsicheren Darstellung der JSON-API.

Zum Beispiel dieser BookDTO.scala Datei definiert ein BookDTO Datenübertragungsobjekt, das nur Typen verwendet, die ohne weitere Definition zu JSON serialisiert werden können:

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] 

} 

und diese JsonExample2.scala Datei zeigt, wie dieses Muster verwenden:

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)) 
    } 

} 
+0

Danke für die ausführliche Erklärung und die tollen Codebeispiele.Das hilft sehr. Aber ich muss noch einen Formatierer pro Klasse registrieren? Es scheint ein wenig wortreich, dies für jede Fallklasse zu tun? Ich habe es in dem von Ihnen angegebenen Link nicht gesehen. –

+2

Ja, wie in der Antwort angegeben, müssen Formatierer (oder Schreiber) für jede Klasse definiert werden, die serialisiert werden muss. Bei Verwendung des DTO-Musters ist dies normalerweise nicht notwendig, da das DTO in Form von einfachen Typen definiert werden kann, die keine spezifischen Serialisierer erfordern. Sehen Sie sich ein Beispiel dieses Musters an, das ich der Antwort hinzugefügt habe. –

+0

importieren Modelle.Formatters._ perfekt! Du hast meinen Tag gerettet, danke Chef (: – oguzhan

Verwandte Themen