Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 用于编码/解码arity 0的密封特征实例的Circe实例?_Scala_Circe - Fatal编程技术网

Scala 用于编码/解码arity 0的密封特征实例的Circe实例?

Scala 用于编码/解码arity 0的密封特征实例的Circe实例?,scala,circe,Scala,Circe,我使用密封特征作为穷举模式匹配的枚举。如果我有case对象,而不是扩展我的trait的case类,我希望编码和解码(via)为一个普通字符串 例如: sealed trait State case object On extends State case object Off extends State val a: State = State.Off a.asJson.noSpaces // trying for "Off" decode[State]("On") // should be

我使用密封特征作为穷举模式匹配的枚举。如果我有case对象,而不是扩展我的trait的case类,我希望编码和解码(via)为一个普通字符串

例如:

sealed trait State
case object On extends State
case object Off extends State

val a: State = State.Off
a.asJson.noSpaces // trying for "Off"

decode[State]("On") // should be State.On

我知道这在0.5.0中是可配置的,但有人能帮我写点东西,让我度过难关,直到它发布吗?

为了突出问题,假设这个ADT:

sealed trait State
case object On extends State
case object Off extends State
circe的通用派生(目前)将产生以下编码:

scala> import io.circe.generic.auto._, io.circe.syntax._
import io.circe.generic.auto._
import io.circe.syntax._

scala> On.asJson.noSpaces
res0: String = {}

scala> (On: State).asJson.noSpaces
res1: String = {"On":{}}
这是因为泛型派生机制是建立在Shapeless的
LabelledGeneric
上的,它将case对象表示为空的
HList
s。这可能永远是默认行为,因为它干净、简单、一致,但并不总是您想要的(正如您所注意到的,即将推出的产品将支持备选方案)

您可以通过为案例对象提供自己的通用实例来覆盖此行为:

import io.circe.Encoder
import shapeless.{ Generic, HNil }

implicit def encodeCaseObject[A <: Product](implicit
  gen: Generic.Aux[A, HNil]
): Encoder[A] = Encoder[String].contramap[A](_.productPrefix)
如果将该值静态类型化为基类型,则故事会有所不同:

scala> (On: State).asJson.noSpaces
res3: String = {"On":"On"}
我们为
State
获得了一个泛型派生实例,它尊重我们为case对象手动定义的泛型实例,但它仍然将它们包装在一个对象中。如果您考虑一下,这会有一定的意义ADT可以包含case类,而case类只能合理地表示为JSON对象,因此使用构造函数名称键方法的对象包装器可以说是最合理的做法

不过,这不是我们能做的唯一事情,因为我们确实静态地知道ADT是包含case类还是只包含case对象。首先,我们需要一个新的类型类来证明ADT只由case对象组成(注意,我在这里假设了一个新的开始,但它应该可以与泛型派生一起工作):

也可以继续写解码器

import cats.data.Xor, io.circe.Decoder

implicit def decodeEnum[A, C <: Coproduct](implicit
  gen: LabelledGeneric.Aux[A, C],
  rie: IsEnum[C]
): Decoder[A] = Decoder[String].emap { s =>
  Xor.fromOption(rie.from(s).map(gen.from), "enum")
}

这正是我们想要的。

当密封特征包含在一个对象中时,它就失效了。我一直在试验,但我希望得到一些关于如何解决这个问题的指导。
import shapeless._
import shapeless.labelled.{ FieldType, field }

trait IsEnum[C <: Coproduct] {
  def to(c: C): String
  def from(s: String): Option[C]
}

object IsEnum {
  implicit val cnilIsEnum: IsEnum[CNil] = new IsEnum[CNil] {
    def to(c: CNil): String = sys.error("Impossible")
    def from(s: String): Option[CNil] = None
  }

  implicit def cconsIsEnum[K <: Symbol, H <: Product, T <: Coproduct](implicit
    witK: Witness.Aux[K],
    witH: Witness.Aux[H],
    gen: Generic.Aux[H, HNil],
    tie: IsEnum[T]
  ): IsEnum[FieldType[K, H] :+: T] = new IsEnum[FieldType[K, H] :+: T] {
    def to(c: FieldType[K, H] :+: T): String = c match {
      case Inl(h) => witK.value.name
      case Inr(t) => tie.to(t)
    }
    def from(s: String): Option[FieldType[K, H] :+: T] =
      if (s == witK.value.name) Some(Inl(field[K](witH.value)))
        else tie.from(s).map(Inr(_))
  }
}
import io.circe.Encoder

implicit def encodeEnum[A, C <: Coproduct](implicit
  gen: LabelledGeneric.Aux[A, C],
  rie: IsEnum[C]
): Encoder[A] = Encoder[String].contramap[A](a => rie.to(gen.to(a)))
import cats.data.Xor, io.circe.Decoder

implicit def decodeEnum[A, C <: Coproduct](implicit
  gen: LabelledGeneric.Aux[A, C],
  rie: IsEnum[C]
): Decoder[A] = Decoder[String].emap { s =>
  Xor.fromOption(rie.from(s).map(gen.from), "enum")
}
scala> import io.circe.jawn.decode
import io.circe.jawn.decode

scala> import io.circe.syntax._
import io.circe.syntax._

scala> (On: State).asJson.noSpaces
res0: String = "On"

scala> (Off: State).asJson.noSpaces
res1: String = "Off"

scala> decode[State](""""On"""")
res2: cats.data.Xor[io.circe.Error,State] = Right(On)

scala> decode[State](""""Off"""")
res3: cats.data.Xor[io.circe.Error,State] = Right(Off)