我试图在scala中使用spray-json来识别转换为Json和返回时Ec2Provider和OpenstackProvider之间的选择。 我希望能够在“提供者”中做出选择,如果这些选择不符合可用的选择,那么它就不应该验证。
我可以在以下代码中看到我的尝试:
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
)
}
不幸的是,它失败了,因为它找不到Provider
抽象类的格式化程序。
test.scala:19: could not find implicit value for evidence parameter of type Infrastructures.JF[Provider]
任何人都有这方面的解决方案吗?
答案 0 :(得分: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
的impls)来查看它是什么类型,然后委托逻辑来处理该类型。
对于写作,我只需要知道哪种实例类型很容易。阅读虽然有点棘手。为了阅读,我要检查对象有多少属性,并且因为两个impls有不同数量的道具,我可以区分哪个是这样的。我在这里做的检查非常简陋,但它表明如果你可以查看Json AST并区分类型,那么你可以选择要反序列化的那个。您的实际检查可以像您所希望的那样简单或复杂,只要它在区分类型方面具有确定性。