2015-04-20 18 views
11

Şu anda XML-RPC iletilerine serileştirmek ve serileştirmek için bir kitaplık uyguluyorum. Neredeyse bitti ama şimdi şekilsiz kullanarak benim şimdiki asProduct yöntemin klişe çıkarmak için çalışıyorum. Benim geçerli kod:Öznitelikler ve tipler ile şekilsiz vaka sınıfları nasıl?

trait Serializer[T] { 
    def serialize(value: T): NodeSeq 
} 

trait Deserializer[T] { 
    type Deserialized[T] = Validation[AnyErrors, T] 
    type AnyErrors = NonEmptyList[AnyError] 
    def deserialize(from: NodeSeq): Deserialized[T] 
} 

trait Datatype[T] extends Serializer[T] with Deserializer[T] 

// Example of asProduct, there are 20 more methods like this, from arity 1 to 22 
def asProduct2[S, T1: Datatype, T2: Datatype](apply: (T1, T2) => S)(unapply: S => Product2[T1, T2]) = new Datatype[S] { 
    override def serialize(value: S): NodeSeq = { 
    val params = unapply(value) 
    val b = toXmlrpc(params._1) ++ toXmlrpc(params._2) 
    b.theSeq 
    } 

    // Using scalaz 
    override def deserialize(from: NodeSeq): Deserialized[S] = (
     fromXmlrpc[T1](from(0)) |@| fromXmlrpc[T2](from(1)) 
    ) {apply} 
} 

Amacım kütüphanenin kullanıcı Demirbaş kodu yazmaya zorlamak olmadan/serisi kaldırılmaya vaka sınıfları seri hale getirmek için olanak sağlamaktır. Şu anda, bağlamda bir Datatype örneğinin olması için yukarıda belirtilen asProduct yöntemini kullanarak vaka sınıfını ve örtülü bir değeri bildirmeniz gerekir. Bu örtük aşağıdaki kodda kullanılır:

def toXmlrpc[T](datatype: T)(implicit serializer: Serializer[T]): NodeSeq = 
    serializer.serialize(datatype) 

def fromXmlrpc[T](value: NodeSeq)(implicit deserializer: Deserializer[T]): Deserialized[T] = 
    deserializer.deserialize(value) 

Bu seri ve tip sınıfları kullanarak deserializing klasik stratejisidir. Bu anda

, ben Jenerik veya LabelledGeneric aracılığıyla hList dava sınıflardan dönüştürmek için nasıl kavramışlardır. Bu dönüşüm ben asProduct2 örnekte olduğu gibi yöntemleri fromXmlrpc ve toXmlrpc çağırabilir nasıl yapmış kez sorundur. Bu nedenle, derleyici toXmlrpc fromXmlrpc ve tatmin herhangi örtük bulamıyorum durumda sınıfında niteliklerin türleri ve, hakkında herhangi bir bilgi yoktur. Bir HList öğesinin tüm öğelerinin bağlamda örtülü bir Datatype olduğunu kısıtlamak için bir yönteme ihtiyacım var. Biçimsiz bir acemi olduğum

, ben bu işlevi almanın en iyi yolu ne olduğunu bilmek istiyorum. Bazı içgörülerim var ama kesinlikle Shapeless kullanarak nasıl yapılacağına dair hiçbir fikrim yok. İdeal durumda sınıfın belirli bir özelliğinin gelen türünü almak ve fromXmlrpc ve toXmlrpc için açıkça bu tür geçmesi için bir yol olacaktır. Bunun nasıl yapılacağını hayal ediyorum.

cevap

15

Önce, HList jenerik serializers yazmak gerekir.

implicit def genericDatatype[T, R](implicit gen: Generic.Aux[T, R], 
            rd: Lazy[Datatype[R]]): Datatype[T] = 
    new Datatype[T] { 
    override def serialize(value: T): NodeSeq = 
     rd.value.serialize(gen.to(value)) 

    override def deserialize(from: NodeSeq): Deserialized[T] = 
     rd.value.deserialize(from).map(rd.from) 
    } 

Not zorundaydım:

implicit def hconsDatatype[H, T <: HList](implicit hd: Datatype[H], 
              td: Datatype[T]): Datatype[H :: T] = 
    new Datatype[H :: T] { 
    override def serialize(value: H :: T): NodeSeq = value match { 
     case h :: t => 
     val sh = hd.serialize(h) 
     val st = td.serialize(t) 
     (sh ++ st).theSeq 
    } 

    override def deserialize(from: NodeSeq): Deserialized[H :: T] = 
     (hd.deserialize(from.head) |@| td.deserialize(from.tail)) { 
     (h, t) => h :: t 
     } 
    } 

implicit val hnilDatatype: Datatype[HNil] = 
    new Datatype[HNil] { 
    override def serialize(value: HNil): NodeSeq = NodeSeq() 
    override def deserialize(from: NodeSeq): Deserialized[HNil] = 
     Success(HNil) 
    } 

Sonra Generic aracılığıyla yapıbozuma edilebilir herhangi bir türü için genel bir seri hale tanımlayabilirsiniz: Yani H :: T ve HNil seri hale getirilmeye belirtmek gerekir ise Lazy kullanın, aksi takdirde iç içe vaka sınıflarınız varsa, bu kod örtük çözüm sürecini kesebilir. "Kapsayıcı genişletme" hatalarını alırsanız, Lazy, örtük parametrelere hconsDatatype ve hnilDatatype ekleyerek de ekleyebilirsiniz.

Bu çalıştığı için Generic.Aux[T, R] bağlantılar keyfi ürün benzeri tip T ve HList tip R.Örneğin, bu durumda sınıfı için

case class A(x: Int, y: String) 

şekilsiz Eğer seri için yetki verebilir

Generic.Aux[A, Int :: String :: HNil] 

Sonuç tipte bir Generic örneğini oluşturur yinelemeli ile HList veri dönüştürme HList için Datatype s tanımlandığı İlk önce Generic. Deserialization benzer şekilde çalışır ancak tersine - önce serileştirilmiş form HList'a okunur ve bu HListGeneric ile gerçek verilere dönüştürülür.

Yukarıdaki NodeSeq API kullanımında birtakım hatalar yapabilmem mümkün, fakat sanırım genel fikri aktarıyor.

LabelledGeneric kullanmak isterseniz, Coproduct s ile temsil edilen mühürlenmiş özellik hiyerarşilerini işlemek istiyorsanız, kod biraz daha karmaşık hale gelir ve daha da fazla olur.

Kitaplığımda, picopickle'da genel seri hale getirme mekanizması sağlamak için şekilsiz kullanıyorum. Bunu, şekilsiz olarak yapan başka bir kütüphanenin farkında değilim. Bazı örneklerin bu kütüphanede ne kadar şekilsiz kullanılabileceğini deneyebilirsiniz, ancak kodun bir kısmı karmaşıktır. Ayrıca, şekilsiz örnekler arasında bir örnek vardır, yani S-expressions.

11

Vladimir'in cevabı harika ve kabul edilen biri olmalı, ancak Shapeless's TypeClass machinery ile bunu biraz daha güzel yapmak da mümkündür.

import scala.xml.NodeSeq 
import scalaz._, Scalaz._ 

trait Serializer[T] { 
    def serialize(value: T): NodeSeq 
} 

trait Deserializer[T] { 
    type Deserialized[T] = Validation[AnyErrors, T] 
    type AnyError = Throwable 
    type AnyErrors = NonEmptyList[AnyError] 
    def deserialize(from: NodeSeq): Deserialized[T] 
} 

trait Datatype[T] extends Serializer[T] with Deserializer[T] 

Biz bu yazabilirsiniz:

import shapeless._ 

object Datatype extends ProductTypeClassCompanion[Datatype] { 
    object typeClass extends ProductTypeClass[Datatype] { 
    def emptyProduct: Datatype[HNil] = new Datatype[HNil] { 
     def serialize(value: HNil): NodeSeq = Nil 
     def deserialize(from: NodeSeq): Deserialized[HNil] = HNil.successNel 
    } 

    def product[H, T <: HList](
     dh: Datatype[H], 
     dt: Datatype[T] 
    ): Datatype[H :: T] = new Datatype[H :: T] { 
     def serialize(value: H :: T): NodeSeq = 
     dh.serialize(value.head) ++ dt.serialize(value.tail) 

     def deserialize(from: NodeSeq): Deserialized[H :: T] = 
     (dh.deserialize(from.head) |@| dt.deserialize(from.tail))(_ :: _) 
    } 

    def project[F, G](
     instance: => Datatype[G], 
     to: F => G, 
     from: G => F 
    ): Datatype[F] = new Datatype[F] { 
     def serialize(value: F): NodeSeq = instance.serialize(to(value)) 
     def deserialize(nodes: NodeSeq): Deserialized[F] = 
     instance.deserialize(nodes).map(from) 
    } 
    } 
} 

hep birlikte düzgün companioned olacak bu yüzden bu tanımlamak emin olun aşağıdaki kurulum Verilen. (Bu durumda sadece String olarak) vaka sınıf üyelerinin türleri için

case class Foo(bar: String, baz: String) 

Ve örneklerini: bir olgu sınıf varsa

Sonra

implicit object DatatypeString extends Datatype[String] { 
    def serialize(value: String) = <s>{value}</s> 
    def deserialize(from: NodeSeq) = from match { 
    case <s>{value}</s> => value.text.successNel 
    case _ => new RuntimeException("Bad string XML").failureNel 
    } 
} 

otomatik türetilmiş bir örneğini almak Foo için:

scala> case class Foo(bar: String, baz: String) 
defined class Foo 

scala> val fooDatatype = implicitly[Datatype[Foo]] 
fooDatatype: Datatype[Foo] = [email protected] 

scala> val xml = fooDatatype.serialize(Foo("AAA", "zzz")) 
xml: scala.xml.NodeSeq = NodeSeq(<s>AAA</s>, <s>zzz</s>) 

scala> fooDatatype.deserialize(xml) 
res1: fooDatatype.Deserialized[Foo] = Success(Foo(AAA,zzz)) 

Bu sa hakkında çalışır Bana Vladimir'in çözümü olarak, ancak Shapeless, tip sınıfının türetme türünün sıkıcı bir özetini oluşturmasına izin verir, böylece ellerinizi Generic ile kirletmeniz gerekmez.

İlgili konular