2013-05-12 8 views
9

Ich versuche, Spray-Json in Scala zu verwenden, um die Wahl zwischen Ec2Provider und OpenstackProvider bei der Konvertierung in Json und zurück zu erkennen. Ich möchte in "Provider" wählen können, und wenn diese Auswahl nicht zu den verfügbaren passt, sollte es nicht validieren.Konvertieren polymorphe Fall Klassen in JSON und zurück

import spray.json._ 
import DefaultJsonProtocol._ 

case class Credentials(username: String, password: String) 
abstract class Provider 
case class Ec2Provider(endpoint: String,credentials: Credentials) extends Provider 
case class OpenstackProvider(credentials: Credentials) extends Provider 
case class Infrastructure(name: String, provider: Provider, availableInstanceTypes: List[String]) 
case class InfrastructuresList(infrastructures: List[Infrastructure]) 

object Infrastructures extends App with DefaultJsonProtocol { 
    implicit val credFormat = jsonFormat2(Credentials) 
    implicit val ec2Provider = jsonFormat2(Ec2Provider) 
    implicit val novaProvider = jsonFormat1(OpenstackProvider) 
    implicit val infraFormat = jsonFormat3(Infrastructure) 
    implicit val infrasFormat = jsonFormat1(InfrastructuresList) 

    println(
    InfrastructuresList(
     List(
     Infrastructure("test", Ec2Provider("nova", Credentials("user","pass")), List("1", "2")) 
    ) 
    ).toJson 
) 
} 

Leider scheitert es, weil es nicht eine Formatierer für Provider abstrakte Klasse finden:

bei dieser Mein Versuch kann in dem folgenden Code zu sehen.

test.scala:19: could not find implicit value for evidence parameter of type Infrastructures.JF[Provider] 

Hat jemand eine Lösung dafür?

Antwort

14

Was Sie tun möchten, ist nicht out of the box (d. H. Über etwas wie Typ Hinweise, die dem Deserializer wissen, welche konkrete Klasse instanziieren), aber es ist sicherlich möglich mit ein wenig Bein Arbeit. Zunächst wird das Beispiel eine vereinfachte Version des Codes verwenden Sie oben geschrieben:

case class Credentials(user:String, password:String) 
abstract class Provider 
case class Ec2Provider(endpoint:String, creds:Credentials) extends Provider 
case class OpenstackProvider(creds:Credentials) extends Provider 
case class Infrastructure(name:String, provider:Provider) 

object MyJsonProtocol extends DefaultJsonProtocol{ 
    implicit object ProviderJsonFormat extends RootJsonFormat[Provider]{ 
    def write(p:Provider) = p match{ 
     case ec2:Ec2Provider => ec2.toJson 
     case os:OpenstackProvider => os.toJson 
    } 

    def read(value:JsValue) = value match{ 
     case obj:JsObject if (obj.fields.size == 2) => value.convertTo[Ec2Provider] 
     case obj:JsObject => value.convertTo[OpenstackProvider] 
    } 
    } 

    implicit val credFmt = jsonFormat2(Credentials) 
    implicit val ec2Fmt = jsonFormat2(Ec2Provider) 
    implicit val openStackFmt = jsonFormat1(OpenstackProvider) 
    implicit val infraFmt = jsonFormat2(Infrastructure) 
} 

object PolyTest { 
    import MyJsonProtocol._ 

    def main(args: Array[String]) { 
    val infra = List(
     Infrastructure("ec2", Ec2Provider("foo", Credentials("me", "pass"))), 
     Infrastructure("openstack", OpenstackProvider(Credentials("me2", "pass2"))) 
    ) 
    val json = infra.toJson.toString 
    val infra2 = JsonParser(json).convertTo[List[Infrastructure]] 
    println(infra == infra2) 
    } 
} 

Um in der Lage sein, zu serialisieren/deserialisieren Instanzen der abstrakten Klasse Provider, ich habe eine benutzerdefinierte Formatter erstellt, wo ich bin Versorgung Operationen zum Lesen und Schreiben Provider Instanzen. Alles, was ich in diesen Funktionen tue, ist jedoch eine einfache Bedingung zu überprüfen (hier binär, da es nur 2 Impls von Provider gibt), um zu sehen, um welchen Typ es sich handelt, und dann an Logik zu delegieren, um diesen Typ zu behandeln.

Zum Schreiben muss ich nur wissen, welcher Instanztyp es ist, was einfach ist. Lesen ist jedoch ein wenig komplizierter. Zum Lesen überprüfe ich wie viele Eigenschaften das Objekt hat und da die beiden Impls unterschiedliche Anzahlen von Requisiten haben, kann ich unterscheiden was auf diese Weise ist. Die Überprüfung, die ich hier mache, ist sehr rudimentär, aber es zeigt den Punkt, dass, wenn Sie den Json AST betrachten und die Typen unterscheiden können, Sie dann auswählen können, welcher zu deserialize. Ihre tatsächliche Überprüfung kann so einfach oder so kompliziert sein, wie Sie möchten, solange es deterministisch ist, die Typen zu unterscheiden.

+0

Als Sie sehr! Genau das habe ich gebraucht! – wernerb

Verwandte Themen