2013-05-12 4 views
9

Json으로 변환 할 때 Ec2Provider와 OpenstackProvider 사이의 선택을 인식하기 위해 scala에서 spray-json을 사용하려고합니다. "제공자"에서 선택권을 부여 할 수 있기를 원합니다. 그리고 그러한 선택 사항이 이용 가능한 것과 맞지 않을 경우 유효성을 검증하면 안됩니다. 이것polymorphic case 클래스를 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 인스턴스를 읽고 쓰는 작업. 이 모든 함수에서 수행하고있는 것은 단순한 조건 (여기서 2 진수는 Provider이므로 이진수는 여기에 있습니다)이 어떤 유형인지 확인한 다음 해당 유형을 처리하는 논리를 위임하는 것입니다.

글을 쓰려면, 어떤 인스턴스 유형인지 쉽게 알아야합니다. 그러나 독서는 조금 더 까다 롭습니다. 독서를 위해, 물건이 얼마나 많은 물건을 가지고 있는지 확인하고 있는데, 두 가지 의미가 서로 다른 수의 소품을 가지고 있기 때문에, 나는이 것을 어느 것이 구별 할 수 있습니다. 내가 여기서 만들고있는 점검은 매우 초보적인 부분이지만, Json AST를보고 유형을 차별화 할 수 있다면, 어느 것을 탈 직렬화할지 선택할 수 있다는 점을 보여줍니다. 실제 검사는 유형을 차별화하는 데있어 결정적 인 한 원하는만큼 간단하거나 복잡 할 수 있습니다.

+0

대단히! 이것은 내가 필요한 것입니다! – wernerb