Circe как Json не кодирует свойства из абстрактного базового класса - PullRequest
0 голосов
/ 25 апреля 2020

Предположим, у меня есть следующий абстрактный базовый класс:

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, отсутствует. Я ожидаю, что встроенный кодер должен нормально работать в этом случае. Мне действительно нужно строить свой собственный?

1 Ответ

2 голосов
/ 25 апреля 2020

Посмотрим, что происходит при генерации энкодера. Circe использует бесформенный для получения своих кодеков, так что достаточно проверить, что решает бесформенный, чтобы ответить на ваш вопрос. Так что в аммоните:

@ 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. Справедливо: бесформенный способ - извлечь все поля, доступные в конструкторе, преобразуя его одним способом, и вернуть все поля обратно через конструктор, если преобразуется другим.

По сути, все деривации классов типов работают таким образом, поэтому вы должны сделать возможным передать _id в качестве конструктора:

abstract class RecordObject {
    val _id: String
}

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

Это поможет производным классам типов выполнить свою работу. Если вы не можете изменить то, как выглядит PersonRecord ... тогда да, вы должны написать свой собственный код c. Хотя я сомневаюсь, что это будет легко, поскольку вы сделали _id неизменным и невозможно установить извне с помощью конструктора, так что это также будет трудно реализовать любым другим способом.

...