获取案例的具体类型 class
Get concrete type of a case class
我有这个:
sealed trait Block
sealed case class Header(param1: String,
param2: String,
...) extends Block
...
(more sealed case classes that follows the same pattern)
稍后,我将这些块分组到一个 Seq 中,如下所示:
val blocks: Seq[Block with Product with Serializable] = Seq(structure.header, structure.body, ...)
我想将每个块序列化为 Json(使用 Play)。我是这样做的:
blocks.map{
x =>
serializeBlock(x)
}
"serializeBlock"的定义:
def serializeBlock[A<:Block](block:A): String = {
block match {
case block: Header => Json.toJson(block).toString()
case block: Body => Json.toJson(block).toString()
... n-times for every possible case class that mixes the block trait
}
}
每个具体块(header、body...)我都有读者和编写者,但是,如您所见,当我在 Seq Scala 中混合这些块时,会将其视为通用类型块,因此我正在对每种可能的块类型进行模式匹配(隐式转换?)。如果我只是在 Map Play 中调用 "Json.toJson",就会抱怨找不到 "block" 类型的 Reader/Writer。
一个"Block"是一个比较大的JSON的片段。我得到 JSON,我将其拆分为合格的 "blocks",然后将其作为字符串保存在数据库中:
"Big" JSON:
{
"header" : {
"param1" : "",
...
},
"body" : {
"param1" : "",
...
}
...
}
块
{
"param1" : "",
...
}
我的问题是:有没有什么方法可以在不重复 n-times "block: type" 的情况下进行序列化图案?我的意思是:有什么方法可以获取该块的具体类型(知道 Seq 的类型是超类 "block" 而不是该块的 "concrete" 类型)?
编辑
我对每个块都有一个 Reader/Writer,如下所示:
implicit val headerReader: Reads[Header] = (
(JsPath \ "param1").read[String] and
(JsPath \ "param2").read[String] and
...
)(Header.apply _)
implicit val headerWriter: Writes[Header] = (
(JsPath \ "param1").write[String] and
(JsPath \ "param2").write[String] and
...
)(unlift(Header.unapply))
编辑 2:
Shapeless有办法解决吗?
编辑 3:
正如 Andrzej Jozwik 所说:"param1" 和 "param2" 是 'wildcard' 参数,我在这里定义了我的 JSON 结构。每个块都有不同的参数。
shapeless' HList 似乎是适合您的解决方案。这是一个看起来非常接近您想要执行的操作的示例:
import shapeless._
sealed trait Block
case class Test1(a: String, b: String) extends Block
object Test1 {
implicit val writes = (
(JsPath \ "a").write[String] and
(JsPath \ "b").write[String]
)(unlift(Test1.unapply))
}
case class Test2(c: String, d: String) extends Block
object Test2 {
implicit val writes =(
(JsPath \ "c").write[String] and
(JsPath \ "d").write[String]
)(unlift(Test2.unapply))
}
object serializeBlock extends Poly1 {
implicit def default[T <: Block](implicit w: Writes[T]) = at[T] { x =>
Json.toJson(x).toString
}
}
val blocks = Test1("hi", "hello") :: Test2("what", "why") :: HNil
blocks.map(serializeBlock).toList // List[String]({"a": "hi", "b": "hello"}, {"c": "what", "d": "why"})
请注意,HList 的每个成员都必须具有可用于该类型的隐式写入。如果不是,您得到的错误就没有太大帮助:
val list = Test1("hi", "hello") :: Test2("a", "b") :: NoWrites("wrong") :: HNil
list.map(serializeBlock)
错误:
could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[serializeBlock.type,shapeless.::[Test1,shapeless.::[Test2,shapeless.::[NoWrites,shapeless.HNil]]]]
这只是意味着无法为您在其上调用 map 的 HList 的某些成员调用 serializeBlock。
在这种情况下,它无法对 NoWrites 对象调用 serializeBlock,因为在当前范围内没有可用的隐式 Writes[NoWrites]。
如果 HList 中的任何对象没有扩展 Block,您将得到类似的错误。
我有这个:
sealed trait Block
sealed case class Header(param1: String,
param2: String,
...) extends Block
...
(more sealed case classes that follows the same pattern)
稍后,我将这些块分组到一个 Seq 中,如下所示:
val blocks: Seq[Block with Product with Serializable] = Seq(structure.header, structure.body, ...)
我想将每个块序列化为 Json(使用 Play)。我是这样做的:
blocks.map{
x =>
serializeBlock(x)
}
"serializeBlock"的定义:
def serializeBlock[A<:Block](block:A): String = {
block match {
case block: Header => Json.toJson(block).toString()
case block: Body => Json.toJson(block).toString()
... n-times for every possible case class that mixes the block trait
}
}
每个具体块(header、body...)我都有读者和编写者,但是,如您所见,当我在 Seq Scala 中混合这些块时,会将其视为通用类型块,因此我正在对每种可能的块类型进行模式匹配(隐式转换?)。如果我只是在 Map Play 中调用 "Json.toJson",就会抱怨找不到 "block" 类型的 Reader/Writer。
一个"Block"是一个比较大的JSON的片段。我得到 JSON,我将其拆分为合格的 "blocks",然后将其作为字符串保存在数据库中:
"Big" JSON:
{
"header" : {
"param1" : "",
...
},
"body" : {
"param1" : "",
...
}
...
}
块
{
"param1" : "",
...
}
我的问题是:有没有什么方法可以在不重复 n-times "block: type" 的情况下进行序列化图案?我的意思是:有什么方法可以获取该块的具体类型(知道 Seq 的类型是超类 "block" 而不是该块的 "concrete" 类型)?
编辑
我对每个块都有一个 Reader/Writer,如下所示:
implicit val headerReader: Reads[Header] = (
(JsPath \ "param1").read[String] and
(JsPath \ "param2").read[String] and
...
)(Header.apply _)
implicit val headerWriter: Writes[Header] = (
(JsPath \ "param1").write[String] and
(JsPath \ "param2").write[String] and
...
)(unlift(Header.unapply))
编辑 2:
Shapeless有办法解决吗?
编辑 3:
正如 Andrzej Jozwik 所说:"param1" 和 "param2" 是 'wildcard' 参数,我在这里定义了我的 JSON 结构。每个块都有不同的参数。
shapeless' HList 似乎是适合您的解决方案。这是一个看起来非常接近您想要执行的操作的示例:
import shapeless._
sealed trait Block
case class Test1(a: String, b: String) extends Block
object Test1 {
implicit val writes = (
(JsPath \ "a").write[String] and
(JsPath \ "b").write[String]
)(unlift(Test1.unapply))
}
case class Test2(c: String, d: String) extends Block
object Test2 {
implicit val writes =(
(JsPath \ "c").write[String] and
(JsPath \ "d").write[String]
)(unlift(Test2.unapply))
}
object serializeBlock extends Poly1 {
implicit def default[T <: Block](implicit w: Writes[T]) = at[T] { x =>
Json.toJson(x).toString
}
}
val blocks = Test1("hi", "hello") :: Test2("what", "why") :: HNil
blocks.map(serializeBlock).toList // List[String]({"a": "hi", "b": "hello"}, {"c": "what", "d": "why"})
请注意,HList 的每个成员都必须具有可用于该类型的隐式写入。如果不是,您得到的错误就没有太大帮助:
val list = Test1("hi", "hello") :: Test2("a", "b") :: NoWrites("wrong") :: HNil
list.map(serializeBlock)
错误:
could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[serializeBlock.type,shapeless.::[Test1,shapeless.::[Test2,shapeless.::[NoWrites,shapeless.HNil]]]]
这只是意味着无法为您在其上调用 map 的 HList 的某些成员调用 serializeBlock。
在这种情况下,它无法对 NoWrites 对象调用 serializeBlock,因为在当前范围内没有可用的隐式 Writes[NoWrites]。
如果 HList 中的任何对象没有扩展 Block,您将得到类似的错误。