2013-05-12 10 views
9

私は、JsonとBackに変換するときにEc2ProviderとOpenstackProviderの間の選択を認識するためにscalaでspray-jsonを使用しようとしています。 私は「プロバイダ」で選択肢を提供できるようにしたいと考えています。そして、それらの選択肢が利用可能なものに適合しない場合、それは検証されるべきではありません。この時多形ケースクラスをjsonに変換して戻す

私の試みは、次のコードで見ることができます:それはProvider抽象クラスのためのフォーマッタを見つけることができないため

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

残念ながら、それは失敗します。

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

誰でもこれを解決する方法はありますか?

答えて

14

あなたがしたいことは、すぐに利用できません(デシリアライザがどの具体的なクラスをインスタンス化するのを許可するタイプのヒントなどを介して)が、それは確かに小さな足の仕事で可能です。まず、例としては、コードの簡易版を使用して、あなたが上記の投稿:

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

抽象クラスProviderの/デシリアライズインスタンスをシリアル化することができるようにするために、私は私が供給していますカスタムフォーマッタを作成しましたProviderインスタンスを読み書きするための操作。私がこれらの関数で行っているのは、どんな型であるかを見て、その型を扱うロジックに委譲する単純な条件(バイナリはProviderの2つだけです)です。

執筆のために、どのインスタンスタイプが簡単かを知るだけでいいです。しかし、読書はややこしいです。読書のために、私はオブジェクトがいくつのプロパティを持っているかをチェックしています、そして、2つのインプルは異なる数の小道具を持っているので、私はこれをどのように区別することができます。私がここで作っているチェックは非常に初歩的ですが、Json ASTを見て型を差別化することができれば、どちらをデシリアライズするかを選ぶことができます。あなたの実際のチェックは、タイプを区別するのが決定的である限り、あなたが好きなだけ簡単にでも複雑にもなります。

+0

非常によかった!これはまさに私が必要としていたものです! – wernerb

関連する問題