将多态案例类转换为 JSON 并反向转换

9
我正在尝试使用Scala中的spray-json来识别在转换为Json和回传时Ec2Provider和OpenstackProvider之间的选择。 我想能够在"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
  )
}

非常遗憾,它失败了,因为找不到Provider抽象类的格式化程序。

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

有人对此有解决方案吗?
1个回答

15

你想要做的事情在开箱即用的情况下不可用(例如通过类型提示允许反序列化程序知道实例化哪个具体类),但是通过一些功夫肯定是可能的。首先,以你上面发布的代码为基础,使用简化版本的示例:

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的实现)来查看它的类型,然后委托给处理该类型的逻辑。
对于写入,我只需要知道它是哪种实例类型,这很容易。但对于读取,稍微有些棘手。因为这两个实现具有不同数量的属性,所以我检查对象有多少属性,从而区分它们。我正在进行的检查非常基础,但它表明了一点,如果您可以查看Json AST并区分类型,那么您就可以选择将其反序列化到哪个类型。实际检查可以像或复杂或简单,只要它在区分类型方面是确定性的即可。

非常感谢!这正是我所需要的! - wernerb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接