Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.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 用鉴别器解码带有嵌套余积的Case类_Scala_Shapeless_Circe - Fatal编程技术网

Scala 用鉴别器解码带有嵌套余积的Case类

Scala 用鉴别器解码带有嵌套余积的Case类,scala,shapeless,circe,Scala,Shapeless,Circe,我有一个如下的设置 case class A(eventType : String, fieldOne : Int) case class B(eventType : String, fieldOne : Int, fieldTwo : Int) type Event = A :+: B :+: CNil case class X(id :String, events : List[Event]) 我收到下面的Json消息,一个带有一个事件的X(B的实例) 如果我使用circe,我可以将其

我有一个如下的设置

case class A(eventType : String, fieldOne : Int)
case class B(eventType : String, fieldOne : Int, fieldTwo : Int)

type Event = A :+: B :+: CNil

case class X(id :String, events : List[Event])
我收到下面的Json消息,一个带有一个事件的X(B的实例)

如果我使用circe,我可以将其解码为X的一个实例,但是在事件列表中,因为A首先到达副产品,所以它会将其解码为A

val actual = X("id", [A("B", 1)])
val expected = X("id", [B("B", 1, 2)])
我希望能够使用eventType作为配置鉴别器,以确定嵌套字段在副产品中成为哪种类型

我想答案就在这里


但是对于我的情况,我似乎不能很好地解决这个问题。

最简单的方法是修改
A
B
的派生解码器,以便在
事件类型
不是正确的值时失败。这将导致副产品解码器自然地找到合适的情况:

import shapeless._
import io.circe.Decoder, io.circe.syntax._
import io.circe.generic.semiauto.deriveDecoder
import io.circe.generic.auto._, io.circe.shapes._

case class A(eventType: String, fieldOne: Int)
case class B(eventType: String, fieldOne: Int, fieldTwo: Int)

type Event = A :+: B :+: CNil

case class X(id: String, events: List[Event])

implicit val decodeA: Decoder[A] = deriveDecoder[A].emap {
  case a @ A("A", _) => Right(a)
  case _ => Left("Invalid eventType")
}

implicit val decodeB: Decoder[B] = deriveDecoder[B].emap {
  case b @ B("B", _, _) => Right(b)
  case _ => Left("Invalid eventType")
}

val doc = """{
  "id" : "id",
  "events" : [
    {
      "eventType" : "B",
      "fieldOne": 1,
      "fieldTwo" : 2
    }
  ]
}"""
然后:

scala> io.circe.jawn.decode[X](doc)
res0: Either[io.circe.Error,X] = Right(X(id,List(Inr(Inl(B(B,1,2))))))
注意,您仍然可以使用自动派生的编码器,您只需要在解码端进行额外检查。(当然,这是假设您确保不使用无效的事件类型构造
A
B
值,但由于您要求将该成员用作鉴别器,这似乎很好。)

更新:如果不想枚举解码器,可以执行以下操作:

import io.circe.generic.decoding.DerivedDecoder

def checkType[A <: Product { def eventType: String }](a: A): Either[String, A] =
  if (a.productPrefix == a.eventType) Right(a) else Left("Invalid eventType")

implicit def decodeSomeX[A <: Product { def eventType: String }](implicit
  decoder: DerivedDecoder[A]
): Decoder[A] = decoder.emap(checkType)
导入io.circe.generic.decoding.DerivedDecoder

def检查类型[A虽然副产品方法很酷,为什么不使用标记方法呢?似乎它完全适合您的需求,您不需要使用任何复杂的隐式机制,很少有人能够维护。我们需要副产品来创建avro模式。但是,无论我们以何种方式来实现这一点,我的副产品目前大约有20个类很快就会升到100?
import io.circe.generic.decoding.DerivedDecoder

def checkType[A <: Product { def eventType: String }](a: A): Either[String, A] =
  if (a.productPrefix == a.eventType) Right(a) else Left("Invalid eventType")

implicit def decodeSomeX[A <: Product { def eventType: String }](implicit
  decoder: DerivedDecoder[A]
): Decoder[A] = decoder.emap(checkType)