【问题标题】:Convert polymorphic case classes to json and back将多态案例类转换为 json 并返回
【发布时间】:2013-05-06 21:52:23
【问题描述】:

我试图在 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]

有人有解决办法吗?

【问题讨论】:

    标签: json scala spray spray-json


    【解决方案1】:

    您想要做的事情不是开箱即用的(即通过诸如类型提示之类的东西,让反序列化器知道要实例化哪个具体类),但是通过一些工作肯定可以做到。首先,示例,使用您上面发布的代码的简化版本:

    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 个 impls)以查看它是什么类型,然后委派给逻辑来处理该类型。

    对于写作,我只需要知道它是哪种实例类型,这很容易。不过阅读有点麻烦。为了阅读,我正在检查对象有多少属性,并且由于两个 impl 有不同数量的道具,我可以区分哪种是这种方式。我在这里所做的检查非常初级,但它表明如果您可以查看 Json AST 并区分类型,那么您可以选择要反序列化的一个。您的实际检查可以根据您的喜好简单或复杂,只要它在区分类型方面是确定性的。

    【讨论】:

    • 非常感谢!这正是我所需要的!
    猜你喜欢
    • 2016-07-27
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多