Черты и сериализация / десериализация - PullRequest
6 голосов
/ 31 июля 2011

Скажем, у меня есть две черты, которые я хотел бы смешать с классом.Каждая черта реализует абстрактный метод, который нужен классу.

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
}

Затем я могу построить бетон M с помощью A или B и сериализовать:

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

Все работает как положено.Но как я могу десериализовать объекты, учитывая тип черты, смешанной с M?Я мог бы вывести имя черты в методе serialize и затем отправить диспетчеру десериализованного метода M имя, но что если у меня есть классы, отличные от M, в которые можно смешать A и B?Затем каждый класс должен будет продублировать поведение десериализации M.Проблема становится еще хуже, если у меня есть несколько черт, которые нужно смешать в объекте, чтобы сделать его конкретным, и у каждого есть своя собственная сериализация / десериализация.Кто-нибудь занимается такой проблемой?

1 Ответ

5 голосов
/ 31 июля 2011

Да, люди имеют. Для этого нужно использовать шаблон классов типов, предложенный Дэвидом Макивером sbinary и Дебасишем Гошем sjson . Трилогия Дебасиша

особенно полезны для всех промежуточных программистов Scala.

Сегодня многие библиотеки применяют эту методологию, включая мою scalaxb . См

Я позаимствовал идею именования у Scala Collections CanBuildFrom и назвал мои классы типов следующим образом:

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]

Редактировать

Не могли бы вы объяснить мне, как система выбирает между "с A" или "с B"?

Используя шаблон класса типов, библиотеки не смешиваются ни A, ни B. Например, это скалярное устройство, которое предоставляет метод с именем scalaxb.fromXML в объекте пакета, определенном следующим образом:

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)
}

Учитывая, что у вас есть XML-документ, и вы хотите демонтировать (десериализовать) его в ipo.Address объект, вы бы вызвали

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

Объект Address остается чистым, используя шаблон класса типов:

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

Как компилятор знает, что делать? Магия - это неявный параметр, требуемый fromXML, который называется implicit format: XMLFormat[A]. Это требует, чтобы у вас было XMLFormat[Address] доступным как неявное значение в области, где вызывается scalaxb.fromXML[ipo.Address](...).

Это стало доступно в коде, сгенерированном scalaxb, потому что он смешивается в XMLProtocol с объектом пакета пакета ipo. И ipo.XMLProtocol определяет

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

Edit2

Я думаю, что начинаю понимать настоящий вопрос. У вас есть объект, состоящий из миксинов признаков, и вы хотите каким-то образом «десериализовать» композицию признаков в другом процессе. Как вы уже писали, вы можете включить некоторые теги для каждой черты и загрузить все, что сможете.

Поскольку я уже писал о шаблонах классов типов, позвольте мне продолжить этот подход. Отличная вещь, связанная с наличием кода сериализации вне объекта, заключается в том, что вы могли бы фактически описать комбинацию смешанных объектов. Предположим, есть черты

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

и вы хотите описать миксин как <obj><foo>1</foo><bar>2</bar></obj>. Вот Суть Я взбил. Я определил экземпляр класса типов для Foo, Bar и Foo with Bar и вызвал

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

вернул

Right(FooWithBar(1, 2))
...