0

假设我有以下抽象基类:

package Models

import reactivemongo.bson.BSONObjectID


abstract class RecordObject {
  val _id: String = BSONObjectID.generate().stringify
}

它由以下具体案例类扩展:

package Models

case class PersonRecord(name: String) extends RecordObject

然后我尝试使用如下代码获取 JSON 字符串:

import io.circe.syntax._
import io.circe.generic.auto._
import org.http4s.circe._
// ...

val person = new PersonRecord(name = "Bob")
println(person._id, person.name) // prints some UUID and "Bob"
println(person.asJso) // {"name": "Bob"} -- what happened to "_id"? 

如您所见,_id: String继承自的属性RecordObject丢失了。我希望内置编码器应该可以很好地用于这个用例。我真的需要自己建造吗?

4

1 回答 1

2

让我们看看在编码器生成中会发生什么。Circe 使用 shapeless 派生其编解码器,因此足以检查 shapeless 解析为什么来回答您的问题。所以在菊石中:

@ abstract class RecordObject {
    val _id: String = java.util.UUID.randomUUID.toString
  }
defined class RecordObject

@ case class PersonRecord(name: String) extends RecordObject
defined class PersonRecord

@  import $ivy.`com.chuusai::shapeless:2.3.3`, shapeless._
import $ivy.$                             , shapeless._

@ Generic[PersonRecord]
res3: Generic[PersonRecord]{type Repr = String :: shapeless.HNil} = ammonite.$sess.cmd3$anon$macro$2$1@1123d461

好的,所以它的String :: HNil. 很公平 - shapeless 所做的是提取构造函数中可用的所有字段,以一种方式转换,如果转换另一种方式,则将所有字段通过构造函数放回。

基本上所有类型类派生都以这种方式工作,因此您应该可以_id作为构造函数传递:

abstract class RecordObject {
    val _id: String
}

case class PersonRecord(
  name: String,
  _id: String = BSONObjectID.generate().stringify
) extends RecordObject

这将有助于类型类派生完成其工作。如果您无法更改PersonRecord外观......那么是的,您必须编写自己的编解码器。虽然我怀疑这会很容易,因为您_id通过构造函数从外部设置不可变且不可能,因此使用任何其他方式也很难实现。

于 2020-04-25T19:02:20.840 回答