Расшифровать неполный ADT с помощью Circe - PullRequest
0 голосов
/ 01 октября 2018

Для case class Apple(color:String, sweetness:Double) я могу определить Decoder[String => Apple] через generic. (Semi) auto или generic.extras. (Semi) auto,

Однако для иерархии запечатанных признаков (ADT) я не могу:

sealed trait Fruit {
 def color:String
}

case class Apple(color:String, sweetness:Double) extends Fruit

sealed trait SpecialFruit extends Fruit

case class Camachile(color:String, burstyness:Double) extends SpecialFruit

case class Langsat(color:String, transparency:Double) extends SpecialFruit

Decoder[String => Fruit] // <--- wont compile

Как мне создать такой декодер?


Обновление Причина, по которой мне нужен такой декодер, заключается в том, что ...Разбор не содержит всех полей.- Получение декодера для пропущенных полей нетривиально.

Последний пункт делает невозможным прохождение через декодер [Fruit]

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

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

package no.kodeworks.kvarg.json

import io.circe.generic.extras.Configuration
import io.circe.{Decoder, HCursor}
import no.kodeworks.kvarg.util._
import shapeless.ops.function.FnFromProduct
import shapeless.ops.union.UnzipFields
import shapeless.{Coproduct, HList, LabelledGeneric, _}

trait AdtConfiguredIncompleteDecoders {
  implicit def decodeIncompleteAdt[
  Missing <: HList
  , Adt
  , Func
  , Subtypes <: Coproduct
  , SubtypeKeys <: HList
  , SubtypeValues <: Coproduct
  , ParamsSubtypes <: HList
  , SubtypeFuncs <: HList
  ]
  (implicit
   func: FnFromProduct.Aux[Missing => Adt, Func],
   sub: LabelledGeneric.Aux[Adt, Subtypes],
   uz: UnzipFields.Aux[Subtypes, SubtypeKeys, SubtypeValues],
   subtypeFuncs: SubtypeFunc.Aux[Missing, SubtypeValues, SubtypeFuncs],
   configuration: Configuration = null
  ): Decoder[Func] = {
    val conf = Option(configuration).getOrElse(Configuration.default)
    val disc = conf.discriminator.getOrElse("type")
    val keys = hlistToList[Symbol](uz.keys()).map(_.name)
      .map(conf.transformConstructorNames)
    val subtypeFuncs0 = hlistToList[Decoder[Func]](subtypeFuncs())
    Decoder.withReattempt {
      case h: HCursor =>
        h.downField(disc).as[String] match {
          case Right(decodedType) =>
            val subtypeFuncDecoder = subtypeFuncs0(keys.indexOf(decodedType))
            subtypeFuncDecoder(h)
        }
    }
  }

  trait SubtypeFunc[P <: HList, A <: Coproduct] extends DepFn0 with Serializable {
    type Out <: HList
  }

  object SubtypeFunc {
    type Aux[P <: HList, A <: Coproduct, Out0 <: HList] = SubtypeFunc[P, A] {
      type Out = Out0}

    implicit def cnilSubtypeFunc[P <: HList]: Aux[P, CNil, HNil] = new SubtypeFunc[P, CNil] {
      type Out = HNil

      override def apply(): HNil = HNil
    }

    implicit def cconsSubtypeFunc[
    Missing <: HList
    , CaseClass
    , Func
    , Rest <: Coproduct]
    (implicit
     func: FnFromProduct.Aux[Missing => CaseClass, Func],
     subtypefuncHead: Decoder[Func],
     subtypeFuncRest: SubtypeFunc[Missing, Rest],
    ): SubtypeFunc.Aux[Missing, CaseClass :+: Rest, Decoder[Func] :: subtypeFuncRest.Out] = {
      new SubtypeFunc[Missing, CaseClass :+: Rest] {
        type Out = Decoder[Func] :: subtypeFuncRest.Out

        override def apply() =
          subtypefuncHead :: subtypeFuncRest()
      }
    }
  }

}

Отрывок из объекта пакета no.kodeworks.kvarg.util:

  def hlistToList[T](hlist: shapeless.HList): List[T] = {
    import shapeless._
    hlist match {
      case HNil => Nil
      case head :: tail =>
        collection.immutable.::(head.asInstanceOf[T], hlistToList[T](tail))
    }
  }
0 голосов
/ 02 октября 2018

С decode[Fruit](jsonString), вот пример:

https://scalafiddle.io/sf/jvySm0B/0

На домашней странице circe есть похожий пример: https://circe.github.io/circe/

...