使用Circe解码不完整的ADT

时间:2018-10-01 19:43:27

标签: json scala abstract-data-type circe

对于case class Apple(color:String, sweetness:Double),我可以通过generic。(semi)auto或generic.extras。(semi)auto来定义Decoder[String => Apple]

但是对于密封的特征等级(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

如何创建这样的解码器?


更新 我之所以需要这样的解码器,是因为 -我解析的json不包含所有字段。 -为丢失的字段获取解码器并非易事。

最后一点使得无法通过Decoder [Fruit]

2 个答案:

答案 0 :(得分:1)

使用decode[Fruit](jsonString),下面是示例:

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

在circe的主页上有一个类似的示例:https://circe.github.io/circe/

答案 1 :(得分:0)

这是我尝试的实现。警告:可能与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))
    }
  }