我目前正在实现一个库来对 XML-RPC 消息进行序列化和反序列化。它几乎完成了,但现在我正在尝试使用Shapeless删除我当前asProduct方法的样板。我当前的代码:
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}
}
我的目标是允许我的库的用户序列化/反序列化案例类,而无需强迫他编写样板代码。目前,您必须使用上述 asProduct 方法声明案例类和隐式 val,才能在上下文中拥有一个 Datatype 实例。此隐式用于以下代码:
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)
这是使用类型类进行序列化和反序列化的经典策略。
此刻,我已经掌握了如何通过Generic或LabelledGeneric 从案例类转换为HList。问题是,一旦我完成了这个转换,我可以如何调用方法fromXmlrpc和toXmlrpc,就像 asProduct2 示例中一样。我没有关于案例类中属性类型的任何信息,因此,编译器找不到任何满足fromXmlrpc和toXmlrpc的隐式。我需要一种方法来约束 HList 的所有元素在上下文中都具有隐式数据类型。
由于我是 Shapeless 的初学者,我想知道获得此功能的最佳方式是什么。我有一些见解,但我绝对不知道如何使用 Shapeless 完成它。理想的情况是有一种方法可以从案例类的给定属性中获取类型,并将此类型显式传递给fromXmlrpc和toXmlrpc。我想这不是可以做到的。