【问题标题】:Serialization of scala functionsscala函数的序列化
【发布时间】:2012-04-03 22:46:48
【问题描述】:

我知道有 Jerkson 和 scalaxb 甚至 Java 的 XStream 能够成功地序列化 scala 的数据。我知道他们可以很好地处理字符串、整数甚至案例类。但是,如果我具有作为其中一个领域的功能?

例如如果我有类似的东西:

case class Foo(bar: (Int) => Boolean, baz: Int) 

这些字段能否以某种方式序列化为 JSON 或 XML(实际上,我不在乎哪个,它们应该是人类可读的,这就是我不想使用 SBinary 的原因)?

编辑

我为什么要这样做? 现在我正在编写决策树的实现。我不想每次都从训练数据中重建这些树,因此我需要对它们进行序列化,而这部分可以用 SBinary 来完成。但此外,如果作为人类我们可以查看序列化树并对其进行分析,那就太好了。

它不像我在标题中写的那么广泛,是的。 我现在的想法是用我自己的格式编写一个自定义序列化程序(例如为 Jerkson),或者写入字符串字段然后解析回来。

但我认为可能有一些非常好的方法来执行此操作。

【问题讨论】:

    标签: scala serialization


    【解决方案1】:

    不,函数不是必须可序列化的......而且可序列化函数不太可能提供人类可读的序列化。

    如果你想提供任何一种功能,恐怕没有任何解决方案。如果可以在您的场景中使用,一种可能的解决方法是构建一个实现 Function[Int, Boolean] 的案例类,从而返回到案例类场景。

    例如,假设您已隔离所有函数检查一个整数是否可以被给定整数整除:

    case class Mod(mod: Int) extends Function[Int, Boolean] { //the extends part is not even require
      def apply(x: Int) = x % mod == 0
    }
    
    case class Foo(bar: Mod, baz: Int)
    

    这显然是超严格的。但恐怕这是你能做到的最好的了。

    根据您的编辑。一个解决方案可能是拥有一种工厂类:

    case class IntTest(humanReadableDescription: String) {
      def apply(x: Int) = IntTest.fromString(humanReadableDescription)(x)
    }
    
    object IntTest {
      private val fromString = Map[String, Function[Int, Boolean]] (
         "mod2" -> {x:Int => x % 2 == 0},
         "is42" -> {x:Int => x == 42}
      )
    }
    
    case class Foo(bar: IntTest, baz: Int)
    

    但是在这里,您将失去类型安全性。

    【讨论】:

    • 你能多说一下案例类加函数的第二种情况吗?
    【解决方案2】:

    从语义上讲,你将如何序列化一个函数?唯一可以想象的方法是对实际实现(即字节码)进行编码,这在两种格式中都是可能的,但它本身就是一个痛苦的世界(类加载、版本控制、性能......)

    简而言之,如果没有额外的上下文,您的问题几乎没有意义。

    【讨论】:

    • 如果你坚持人类可读性,你总是可以使用 scalap 或其他任何东西反编译你的函数并包含源代码,但我仍然坚持认为你需要 /very/ 充分的理由来解释这种复杂性。
    • 我已经为问题添加了上下文
    • 现在更有意义了。也许案例类组合是最好的;无论如何,您最好编写一个自定义序列化程序(使用 lift-json 相当容易;不确定 Jerkson 等)来呈现和解析 AST。解析器组合器在这里非常有用,但真正干净的解决方案需要一些认真的工作。
    【解决方案3】:

    是的。这是可能的,而且让我大吃一惊:

    import java.io._
    
    def write[A](obj: A): Array[Byte] = {
      val bo = new ByteArrayOutputStream()
      new ObjectOutputStream(bo).writeObject(obj)
      bo.toByteArray
    }
    
    def read(bytes:Array[Byte]): Any = {
      new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject()
    }
    
    
    read(write( {a:Int => a+1} )).asInstanceOf[ Function[Int,Int] ](5) // == 6
    
    // And case class serialization
    case class XF(f:(Int => Int)) extends Serializable
    
    read(write(XF( {a:Int => a+1} ))).asInstanceOf[XF].f(5) // == 6 
    

    注意。不适用于在外部类或函数范围内定义的类。 (这是有道理的,因为您可能并不是要序列化对外部类的引用)。

    非常酷,但请注意,当加载对象时,JVM 要求加载的类引用在类路径中可用。 (这不是通用的序列化路径,因为读者还需要访问被反序列化的编译类文件)

    【讨论】:

    • 注释掉代码后可以反序列化函数是不正确的,类需要在类路径上,就像任何其他反序列化的对象一样
    猜你喜欢
    • 2018-05-28
    • 2013-01-21
    • 2010-10-19
    • 2012-09-17
    • 2011-12-22
    • 2011-12-25
    • 1970-01-01
    • 1970-01-01
    • 2012-11-23
    相关资源
    最近更新 更多