无形。参考 1 个构造函数参数的所有情况 类
Shapeless. Refer to all case classes of 1 constructor argument
我正在尝试对 1 个参数构造函数的所有情况 类 进行概括,以使用 shapeless 为它们创建 Json 序列化程序。
到目前为止我所做的是:
object ShapelessJsonOFormat extends ProductTypeClassCompanion[OFormat] {
object typeClass extends ProductTypeClass[OFormat] {
override def product[H, T <: HList](ch: OFormat[H], ct: OFormat[T]): OFormat[H :: T] = new OFormat[H :: T] {
override def reads(json: JsValue): JsResult[H :: T] =
for {
h <- ch.reads(json)
t <- ct.reads(json)
} yield h :: t
override def writes(o: H :: T): JsObject = ch.writes(o.head) ++ ct.writes(o.tail)
}
override def emptyProduct: OFormat[HNil] = new OFormat[HNil] {
override def reads(json: JsValue): JsResult[HNil] = JsSuccess(HNil)
override def writes(o: HNil): JsObject = JsObject.empty
}
override def project[F, G](
instance: => OFormat[G],
to: (F) => G,
from: (G) => F): OFormat[F] = new OFormat [F] {
override def writes(o: F): JsObject = instance.writes(to(o))
override def reads(json: JsValue): JsResult[F] = instance.reads(json).map(from)
}
}
}
我没试过,但不是我想要的。问题是扩展 ProductTypeClassCompanion 意味着对所有 HList 进行泛化,以便能够 "add" 两个 json 我被迫在这里玩 Json 对象(参见 override def writes(o: H :: T): JsObject = ch.writes(o.head) ++ ct.writes(o.tail)
中的 ++
).我真正需要的是泛化所有 T :: HNil
类型的 HList。这可能吗?否则,我将被迫将我的所有案例 类 扩展 Product1[T]
.
如果您想将其限制为仅适用于单参数大小写-类,则您不能使用ProductTypeClass
。它的目的是归纳推广到 all 任意数量的产品。你想坚持 arity 1,所以你有冲突。您只需要自己编写所有内容,而无需为您提供(小)样板 ProductTypeclass
摘要。
object ShapelessJsonOFormat {
implicit def caseHListArity1[Head](implicit
headFmt: Lazy[OFormat[Head]] // Use Lazy in some places to get around the (incorrect) implicit divergence check in the compiler
): OFormat[Head :: HNil] = new OFormat[Head :: HNil] {
// ... serialize a Head :: HNil given headFmt.value: OFormat[Head]
}
implicit def caseGeneric[I, O](implicit
gen: Generic.Aux[I, O],
oFmt: Lazy[OFormat[O]]
): OFormat[I] = new OFormat[I] {
// ... serialize an I given a Generic I => O and oFmt.value: OFormat[O]
}
}
从表面上看,您的主要抱怨是您需要在元数抽象版本中将多个 JSONObject
组合在一起。这是因为您使用的是 Generic
,而不是 LabelledGeneric
,因此您无法为产品元素提供字段名称,因此它们在一个级别上混在一起。您通常可以为此使用 LabelledProductTypeClass
,但它在这里 相当 不起作用,因此我们只能使用自己的样板。此版本适用于任何元数。
type OFormatObject[A] = OFormat[A] { def write(value: A): JsObject }
object ShapelessJsonOFormat {
implicit val caseHNil: OFormatObject[HNil] = new OFormat[HNil] {
override def read(json: JsValue) = HNil
override def write(value: HNil) = JsObject.empty
}
implicit def caseHCons[
HeadName <: Symbol,
Head,
Tail <: HList
](implicit
headFmt: Lazy[OFormat[Head]],
nameWitness: Witness.Aux[HeadName],
tailFmt: Lazy[OFormatObject[Tail]]
): OFormatObject[FieldType[HeadName, Head] :: Tail]
= new OFormat[FieldType[HeadName, Head] :: Tail] {
private val fieldName = nameWitness.value.name // Witness[_ <: Symbol] => Symbol => String
override def read(json: JsValue): FieldType[HeadName, Head] :: Tail = {
val headObj = json.asJsObject.get(fieldName)
val head = headFmt.read(headObj)
val tail = tailFmt.read(json)
field[HeadName](head) :: tail
}
override def write(value: FieldType[HeadName, Head] :: Tail): JsObject = {
val tail = tailFmt.write(value.tail)
val head = headFmt.write(value.head)
tail + JsObject(fieldName -> head) // or similar
}
}
implicit def caseLabelledGeneric[I, O](implicit
gen: LabelledGeneric.Aux[I, O],
oFmt: Lazy[OFormatObject[O]]
): OFormatObject[I] = new OFormat[I] {
override def read(json: JsValue): I = gen.from(oFmt.value.read(json))
override def write(value: I): JsObject = oFmt.value.write(gen.to(value))
}
}
这里的思路是用细化OFormatObject
讲保证write
JsObject
的OFormat
。有一个 OFormatObject[HNil]
就是 { read = _ => HNil; write = _ => {} }
。如果有一个 Head
(OFormat[Head]
) 的序列化器,一个 Tail
(OFormatObject[Tail]
) 的对象序列化器,并且我们有一些表示 [=24] 字段名称的单例类型=](类型参数HeadName <: Symbol
,在Witness.Aux[HeadName]
中实现),那么caseHCons
会产生一个OFormatObject[FieldName[HeadName, Head] :: Tail]
,看起来像{ read = { headName: head, ..tail } => head :: tail; write = head :: tail => { headName: head, ..tail }
。然后我们使用 caseLabelledGeneric
将 HList
s-with-FieldType
s 的解决方案带入一般情况 类.
我正在尝试对 1 个参数构造函数的所有情况 类 进行概括,以使用 shapeless 为它们创建 Json 序列化程序。
到目前为止我所做的是:
object ShapelessJsonOFormat extends ProductTypeClassCompanion[OFormat] {
object typeClass extends ProductTypeClass[OFormat] {
override def product[H, T <: HList](ch: OFormat[H], ct: OFormat[T]): OFormat[H :: T] = new OFormat[H :: T] {
override def reads(json: JsValue): JsResult[H :: T] =
for {
h <- ch.reads(json)
t <- ct.reads(json)
} yield h :: t
override def writes(o: H :: T): JsObject = ch.writes(o.head) ++ ct.writes(o.tail)
}
override def emptyProduct: OFormat[HNil] = new OFormat[HNil] {
override def reads(json: JsValue): JsResult[HNil] = JsSuccess(HNil)
override def writes(o: HNil): JsObject = JsObject.empty
}
override def project[F, G](
instance: => OFormat[G],
to: (F) => G,
from: (G) => F): OFormat[F] = new OFormat [F] {
override def writes(o: F): JsObject = instance.writes(to(o))
override def reads(json: JsValue): JsResult[F] = instance.reads(json).map(from)
}
}
}
我没试过,但不是我想要的。问题是扩展 ProductTypeClassCompanion 意味着对所有 HList 进行泛化,以便能够 "add" 两个 json 我被迫在这里玩 Json 对象(参见 override def writes(o: H :: T): JsObject = ch.writes(o.head) ++ ct.writes(o.tail)
中的 ++
).我真正需要的是泛化所有 T :: HNil
类型的 HList。这可能吗?否则,我将被迫将我的所有案例 类 扩展 Product1[T]
.
如果您想将其限制为仅适用于单参数大小写-类,则您不能使用ProductTypeClass
。它的目的是归纳推广到 all 任意数量的产品。你想坚持 arity 1,所以你有冲突。您只需要自己编写所有内容,而无需为您提供(小)样板 ProductTypeclass
摘要。
object ShapelessJsonOFormat {
implicit def caseHListArity1[Head](implicit
headFmt: Lazy[OFormat[Head]] // Use Lazy in some places to get around the (incorrect) implicit divergence check in the compiler
): OFormat[Head :: HNil] = new OFormat[Head :: HNil] {
// ... serialize a Head :: HNil given headFmt.value: OFormat[Head]
}
implicit def caseGeneric[I, O](implicit
gen: Generic.Aux[I, O],
oFmt: Lazy[OFormat[O]]
): OFormat[I] = new OFormat[I] {
// ... serialize an I given a Generic I => O and oFmt.value: OFormat[O]
}
}
从表面上看,您的主要抱怨是您需要在元数抽象版本中将多个 JSONObject
组合在一起。这是因为您使用的是 Generic
,而不是 LabelledGeneric
,因此您无法为产品元素提供字段名称,因此它们在一个级别上混在一起。您通常可以为此使用 LabelledProductTypeClass
,但它在这里 相当 不起作用,因此我们只能使用自己的样板。此版本适用于任何元数。
type OFormatObject[A] = OFormat[A] { def write(value: A): JsObject }
object ShapelessJsonOFormat {
implicit val caseHNil: OFormatObject[HNil] = new OFormat[HNil] {
override def read(json: JsValue) = HNil
override def write(value: HNil) = JsObject.empty
}
implicit def caseHCons[
HeadName <: Symbol,
Head,
Tail <: HList
](implicit
headFmt: Lazy[OFormat[Head]],
nameWitness: Witness.Aux[HeadName],
tailFmt: Lazy[OFormatObject[Tail]]
): OFormatObject[FieldType[HeadName, Head] :: Tail]
= new OFormat[FieldType[HeadName, Head] :: Tail] {
private val fieldName = nameWitness.value.name // Witness[_ <: Symbol] => Symbol => String
override def read(json: JsValue): FieldType[HeadName, Head] :: Tail = {
val headObj = json.asJsObject.get(fieldName)
val head = headFmt.read(headObj)
val tail = tailFmt.read(json)
field[HeadName](head) :: tail
}
override def write(value: FieldType[HeadName, Head] :: Tail): JsObject = {
val tail = tailFmt.write(value.tail)
val head = headFmt.write(value.head)
tail + JsObject(fieldName -> head) // or similar
}
}
implicit def caseLabelledGeneric[I, O](implicit
gen: LabelledGeneric.Aux[I, O],
oFmt: Lazy[OFormatObject[O]]
): OFormatObject[I] = new OFormat[I] {
override def read(json: JsValue): I = gen.from(oFmt.value.read(json))
override def write(value: I): JsObject = oFmt.value.write(gen.to(value))
}
}
这里的思路是用细化OFormatObject
讲保证write
JsObject
的OFormat
。有一个 OFormatObject[HNil]
就是 { read = _ => HNil; write = _ => {} }
。如果有一个 Head
(OFormat[Head]
) 的序列化器,一个 Tail
(OFormatObject[Tail]
) 的对象序列化器,并且我们有一些表示 [=24] 字段名称的单例类型=](类型参数HeadName <: Symbol
,在Witness.Aux[HeadName]
中实现),那么caseHCons
会产生一个OFormatObject[FieldName[HeadName, Head] :: Tail]
,看起来像{ read = { headName: head, ..tail } => head :: tail; write = head :: tail => { headName: head, ..tail }
。然后我们使用 caseLabelledGeneric
将 HList
s-with-FieldType
s 的解决方案带入一般情况 类.