2016-04-08 2 views
1

Mit Scala Generics Ich versuche, einige allgemeine Funktionen in meiner Play-Anwendung zu abstrahieren. Die Funktionen geben Seq s mit Objekten zurück, die aus einem REST JSON-Dienst deserialisiert wurden.Scala Funktion Rückgabetyp basierend auf generischen

Die Abruf- und Deserialisierungslogik ist in eine einzige Funktion gepackt, die Generics verwendet.

private def getByEndpoint[T](endpoint: String): Future[Seq[T]] = { 

    ws.url(endpoint) 
     .get() 
     .map(rsp => rsp.json) 
     .flatMap { json => 
     json.validate[Seq[T]] match { 
      case s: JsSuccess[Seq[T]] => 
      Future.successful(s.get) 
      case e: JsError => 
      Future.failed(new RuntimeException(s"Get by endpoint JSON match failed: $e")) 
     } 
     } 

} 

Problem ist, ich bekomme "No Json deserializer found for type Seq[T]. Try to implement an implicit Reads or Format for this type.". Ich bin sicher, dass ich nicht T richtig in Seq[T] (nach meiner C#/Java-Erinnerungen mindestens) verwenden, aber ich kann keine Ahnung finden, wie man es in Scala richtig macht. Alles funktioniert wie erwartet, ohne Generika zu verwenden.

+1

Versuchen Sie 'def getByEndpoint [T]' auf 'def getByEndpoint [T: Format]' zu ändern? Wenn das für Sie funktioniert, schreibe ich Ihnen gerne eine Antwort, in der Sie erklären warum. –

+0

Es tut! Meine Vermutung ist, dass es etwas mit dem 'Json.format' zu tun hat, das ich an den Modellen eingestellt habe. Können Sie bitte eine Antwort posten, damit ich sie als Antwort markieren kann? – giannoug

Antwort

2

Abspielen JSON verwendet type classes, um Informationen darüber zu erfassen, welche Typen wie und von JSON zu (und) serialisiert werden können. Wenn Sie einen impliziten Wert vom Typ Format[Foo] im Geltungsbereich haben, wird dies als Instanz der Format-Klasse für Foo bezeichnet.

Der Vorteil dieses Ansatzes besteht darin, dass es uns ermöglicht, generische Typen einzuschränken (und diese Einschränkungen zur Kompilierungszeit prüfen zu lassen), die nicht vom Subtyping abhängig sind. Zum Beispiel gibt es keine Möglichkeit, dass die Standardbibliothek String jemals eine Art von Jsonable Eigenschaft ausbaut, die Play (oder irgendeine andere Bibliothek) bieten könnte, also brauchen wir eine Art zu sagen "wir wissen, wie man String s als JSON codiert" t involvieren String einen Subtyp einer Eigenschaft, die wir selbst definiert haben.

Im Play JSON können Sie dies durch die Definition implizite Format Instanzen und spielen sich für Sie viele von ihnen bietet (zum Beispiel, wenn Sie eine für T haben, wird es Ihnen eine für Seq[T] geben). Die validate Methode auf JsValue erfordert eine dieser Instanzen (eigentlich ein Subtyp von Format, Reads, aber das ist hier nicht besonders relevant) für seinen Typ Parameter-Seq[T] in diesem Fall-und es wird nicht kompilieren, wenn der Compiler diese Instanz finden kann.

Sie diese Instanz durch Hinzufügen der Beschränkung auf Ihre eigene generische Methode zur Verfügung stellen kann:

private def getByEndpoint[T: Format](endpoint: String): Future[Seq[T]] = { 
    ... 
} 

Jetzt mit der T: Format Syntax Sie haben angegeben, dass es eine Format Instanz für T sein muss (auch wenn Sie don 't Constraint T in irgendeiner anderen Weise), so dass der Compiler weiß, wie die Format Instanz für Seq[T], dass die json.validate[Seq[T]] Aufruf erfordert.