简体   繁体   English

获取案例类的具体类型

[英]Get concrete type of a case class

I have this: 我有这个:

sealed trait Block

sealed case class Header(param1: String,
                                      param2: String,
                                      ...) extends Block

... 
(more sealed case classes that follows the same pattern)

Later, i'm grouping this blocks in a Seq, as follows: 后来,我将这些块分组到Seq中,如下所示:

val blocks: Seq[Block with Product with Serializable] = Seq(structure.header, structure.body, ...)

And i want to serialize every block as Json (with Play). 我想将每个块序列化为Json(使用Play)。 I'm doing it as follows: 我这样做如下:

blocks.map{
      x =>
          serializeBlock(x)
    }

Definition of "serializeBlock": “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
    }
}

I have readers and writers for every concrete block (header, body...) but, as you see, when i mix these blocks in a Seq Scala treats it as generic type Block, for this reason i'm doing the pattern matching with every possible block type (implicit casting?). 我有每个具体块的读者和作者(标题,正文...)但是,如你所见,当我在Seq Scala中混合这些块时将其视为泛型类型Block,因此我正在进行模式匹配每种可能的块类型(隐式转换?)。 If i simply call "Json.toJson" in the Map Play complains about not finding a Reader/Writer for "block" type. 如果我只是在Map Play中调用“Json.toJson”,则抱怨没有找到“块”类型的读/写器。

A "Block" is a fragment of a relatively big JSON. “块”是相对较大的JSON的片段。 I'm getting the JSON, i'm splitting it in qualified "blocks" and then i'm saving it as a String in a Database: 我正在获取JSON,我将它分成合格的“块”然后我将其保存为数据库中的字符串:

"Big" JSON : “大”JSON

{
    "header" : {
        "param1" : "",
        ...
    },
    "body" : {
        "param1" : "",
        ...
    }
    ...
}

Blocks

{
    "param1" : "",
    ...
}

My question is: Is there any way to do the serialization without repeating n-times the " block: type " pattern? 我的问题是:有没有办法进行序列化而不重复n次“ 块:类型 ”模式? I mean: is there any way to get the concrete type of that block (knowing that the Seq is typed as superclass "block" and not as the "concrete" type of that block)? 我的意思是:有没有办法获得该块的具体类型(知道Seq被键入超类“块”而不是该块的“具体”类型)?

EDIT 编辑

I have a Reader/Writer for every block as follows: 我为每个块都有一个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))

EDIT 2: 编辑2:

Is Shapeless a way to solve this? Shapeless是一种解决这个问题的方法吗?

EDIT 3: 编辑3:

As Andrzej Jozwik noted: "param1" and "param2" are 'wildcard' params that i've used to define my JSON Structure here. 正如Andrzej Jozwik所说:“param1”和“param2”是'wildcard'参数,我在这里用来定义我的JSON结构。 Every block has different params. 每个区块都有不同的参数。

shapeless' HList seems like a possible solution for you. 没有形状的'HList似乎是一个可能的解决方案。 Here's an example that seems pretty close to what you want to do: 这是一个看起来非常接近你想要做的例子:

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

Note that each member of the HList must have an implicit Writes available for that type. 请注意,HList的每个成员都必须具有可用于该类型的隐式Writer。 If it doesn't the error you get isn't wildly helpful: 如果不是你得到的错误没有太大的帮助:

val list = Test1("hi", "hello") :: Test2("a", "b") :: NoWrites("wrong") :: HNil
list.map(serializeBlock)

with the error: 有错误:

could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[serializeBlock.type,shapeless.::[Test1,shapeless.::[Test2,shapeless.::[NoWrites,shapeless.HNil]]]]

This just means that it is not possible to call serializeBlock for some member of the HList you called map on. 这只是意味着无法为您调用map on的HList的某个成员调用serializeBlock。

In this case, it can't call serializeBlock on the NoWrites object because there is no implicit Writes[NoWrites] available in the current scope. 在这种情况下,它无法在NoWrites对象上调用serializeBlock,因为当前作用域中没有可用的隐式Writes [NoWrites]。

You will get a similar error if any object in the HList does not extend Block. 如果HList中的任何对象未扩展Block,您将收到类似的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM