特征和序列化/反序列化 [英] Traits and serialization/deserialization

查看:57
本文介绍了特征和序列化/反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想将两个特征混合到一个类中.每个特征都实现了类需要的抽象方法.

trait Writable {def 序列化(输出:java.io.DataOutput)}trait T1 扩展了 Writable特质 A 扩展了 T1 {值 aNum: Int抽象覆盖 def serialize(out: java.io.DataOutput) = {超级序列化(输出)println("序列化")out.writeInt(aNum)}def action = println("一个动作")}特征 B 扩展了 T1 {val bNum: 整数抽象覆盖 def serialize(out: java.io.DataOutput) = {超级序列化(输出)println("B 序列化")out.writeInt(bNum)}def action = println("B action")}抽象类 M[CT1 <: T1](val mNum: Int) extends Writable {这:M[CT1] 与 T1 =>def serialize(out: java.io.DataOutput) = {println("M 序列化")out.writeInt(mNum)}明确行动}

然后我可以用 A 或 B 构造一个具体的 M 并序列化:

scala>val m1 = new M[A](10) with A { val aNum = 20 }m1:M[A] 与 A = $anon$1@67c1e630标度>val m2 = new M[B](20) with B { val bNum = 30 }m2:M[B] 与 B = $anon$1@c36f58e标度>val out = new java.io.DataOutputStream(new java.io.ByteArrayOutputStream())输出:java.io.DataOutputStream = java.io.DataOutputStream@87afebf标度>m1.serialize(out)M序列化一个序列化标度>m2.serialize(out)M序列化B 连载

一切都按预期进行.但是如何在尊重混合到 M 中的特征类型的同时反序列化对象?我可以在 serialize 方法中输出特征的名称,然后在名称上调度 M 的反序列化方法,但是如果我有 M 以外的类可以混合 A 和 B 怎么办?然后,每个类都必须复制 M 的调度反序列化的行为.如果我有多个特征需要混合到一个对象中以使其具体,并且每个特征都有自己的自定义序列化/反序列化要做,那么问题会变得更糟.有人解决过这样的问题吗?

解决方案

是的,人们有.要走的路是使用 David MacIver 的 sbinary 和 Debasish Ghosh 的 sjson.德巴什的三部曲

对所有中级 Scala 程序员特别有用.

今天,许多图书馆都在采用这种方法,包括我的 scalaxb.见

我借鉴了 Scala Collections 的 CanBuildFrom 的命名思想,并将我的类型类命名如下:

trait CanReadXML[A] {def 读取(序列:scala.xml.NodeSeq):Either[String, A]}特征 CanWriteXML[A] {def writes(obj: A, namespace: Option[String], elementLabel: Option[String],范围:命名空间绑定,类型属性:布尔):NodeSeq}trait XMLFormat[A] 用 CanReadXML[A] 扩展了 CanWriteXML[A]

编辑:

<块引用>

你能向我解释一下框架是如何在with A"或with B"之间做出选择的吗?

使用类型类模式,库既不混合A 也不混合B.以scalaxb为例,它在包对象中提供了一个名为scalaxb.fromXML的方法,定义如下:

def fromXML[A](seq: NodeSeq, stack: List[ElemName] = Nil)(隐式格式:XMLFormat[A]):A = format.reads(seq, stack) match {case Right(a) =>一种情况左(a)=>抛出新的 ParserFailure(a)}

鉴于您有 XML 文档,并且想要将其解组(反序列化)为 ipo.Address 对象,您可以调用

scalaxb.fromXML[ipo.Address](<name>Foo</name><street>1537 Paper Street</street><city>威尔明顿</city></shipTo>)

Address 对象使用类型类模式保持纯:

case class Address(name: String, street: String, city: String)

编译器如何知道要做什么?神奇之处在于fromXML 所需的隐式参数,称为隐式格式:XMLFormat[A].这要求您在调用 scalaxb.fromXML[ipo.Address](...) 的范围内将 XMLFormat[Address] 作为隐式值提供.>

这在 scalaxb 生成的代码中可用,因为它将 XMLProtocol 混合到 ipo 包的包对象中.而 ipo.XMLProtocol 定义了

隐式惰性 val IpoAddressFormat: scalaxb.XMLFormat[ipo.Address] = new DefaultIpoAddressFormat {}

Edit2:

我想我开始理解实际问题了.您有一个由特征混合组成的对象,并且您想以某种方式反序列化"其他进程上的特征组合.正如您所写,您可以为每个特征包含一些标签,并加载任何您可以加载的内容.

到目前为止我已经写了类型类模式,让我继续这个方法.在对象的外部序列化代码的巧妙之处在于,您实际上可以描述对象的混合组合.假设有特征

trait Foo { def foo: Int }trait Bar { def bar: Int }

并且您想将 mixin 描述为 <obj><foo>1</foo><bar>2</bar></obj>.这是我整理的 gist.我为 FooBarFoo with Bar 定义了类型类实例,并调用了

Def.fromXML[Foo with Bar](<obj><foo>1</foo><bar>2</bar></obj>)

返回的

Right(FooWithBar(1, 2))

Say I have two traits that I would like to mixin to a class. The traits each implement an abstract method that the class needs.

trait Writable {
    def serialize(out: java.io.DataOutput)
}

trait T1 extends Writable

trait A extends T1 {
   val aNum: Int
   abstract override def serialize(out: java.io.DataOutput) = {
       super.serialize(out)
       println("A serialize")
       out.writeInt(aNum)
   }

   def action = println("A action")
}

trait B extends T1 {
   val bNum: Int
   abstract override def serialize(out: java.io.DataOutput) = {
       super.serialize(out)
       println("B serialize")
       out.writeInt(bNum)
   }

   def action = println("B action")
}

abstract class M[CT1 <: T1](val mNum: Int) extends Writable {
   this: M[CT1] with T1 =>
   def serialize(out: java.io.DataOutput) = {
       println("M serialize")
       out.writeInt(mNum)
   }

   def action
}

I can then construct a concrete M with either A or B and serialize:

scala> val m1 = new M[A](10) with A { val aNum = 20 }
m1: M[A] with A = $anon$1@67c1e630

scala> val m2 = new M[B](20) with B { val bNum = 30 }
m2: M[B] with B = $anon$1@c36f58e

scala> val out = new java.io.DataOutputStream(new java.io.ByteArrayOutputStream())
out: java.io.DataOutputStream = java.io.DataOutputStream@87afebf

scala> m1.serialize(out)
M serialize
A serialize

scala> m2.serialize(out)
M serialize
B serialize

Everything works as expected. But how do I deserialize the objects while respecting the type of trait that is mixed into M? I could output the trait's name in the serialize method and then have M's deserialize method dispatch on the name but what if I have classes other than M that A and B can be mixed into? Then, each class would have to duplicate the behavior of M's dispatching deserialize. The problem gets even worse if I have multiple traits that need to get mixed into an object to make it concrete and each has it's own custom serialization/deserialization to do. Anyone tackle an issue like this?

解决方案

Yes, people have. The way to go is to use typeclass pattern championed by David MacIver's sbinary and Debasish Ghosh's sjson. Debasish's trilogy

are especially useful to all intermediate Scala programmers.

Today, many of the libraries are adopting this methodology including mine scalaxb. See

I've borrowed the naming idea from Scala Collections' CanBuildFrom and named my typeclasses as follows:

trait CanReadXML[A] {
  def reads(seq: scala.xml.NodeSeq): Either[String, A]
}

trait CanWriteXML[A] {
  def writes(obj: A, namespace: Option[String], elementLabel: Option[String],
      scope: NamespaceBinding, typeAttribute: Boolean): NodeSeq
}

trait XMLFormat[A] extends CanWriteXML[A] with CanReadXML[A]

Edit:

Could you explain to me how the framework chooses between "with A" or "with B"?

Using the typeclass pattern, the libraries mixes in neither A nor B. To take scalaxb for example, it provides a method called scalaxb.fromXML in the package object defined as follows:

def fromXML[A](seq: NodeSeq, stack: List[ElemName] = Nil)
              (implicit format: XMLFormat[A]): A = format.reads(seq, stack) match {
  case Right(a) => a
  case Left(a) => throw new ParserFailure(a)
}

Given that you have XML document, and you want to unmarshal (deserialize) it to ipo.Address object, you would call

scalaxb.fromXML[ipo.Address](<shipTo xmlns="http://www.example.com/IPO">
  <name>Foo</name>
  <street>1537 Paper Street</street>
  <city>Wilmington</city>
</shipTo>)

The Address object stays pure using the typeclass pattern:

case class Address(name: String, street: String, city: String)

How does the compiler know what to do? The magic is the implicit parameter required by fromXML called implicit format: XMLFormat[A]. This requires that you have XMLFormat[Address] available as an implicit value within the scope where scalaxb.fromXML[ipo.Address](...) is called.

This made available in the code generated by scalaxb because it mixes in XMLProtocol into the package object of ipo package. And ipo.XMLProtocol defines

implicit lazy val IpoAddressFormat: scalaxb.XMLFormat[ipo.Address] = new DefaultIpoAddressFormat {}

Edit2:

I think I am starting to understand the actual question. You have an object consisting of trait mixins, and you want to somehow "deserialize" the trait composition on other process. As you wrote, you could include some tag for each traits, and load whatever you can.

Since I've written so far on typeclass pattern, let me continue with the approach. The neat thing about having serialization code outside of the object is that you could actually describe mixin combination of the object. Suppose there are traits

trait Foo { def foo: Int }
trait Bar { def bar: Int }

and you want to describe the mixin as <obj><foo>1</foo><bar>2</bar></obj>. Here's a gist I whipped up. I defined typeclass instance for Foo, Bar, and Foo with Bar, and called

Def.fromXML[Foo with Bar](<obj><foo>1</foo><bar>2</bar></obj>)

which returned

Right(FooWithBar(1, 2))

这篇关于特征和序列化/反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆