Почему он не декодируется в тип ADT? - PullRequest
3 голосов
/ 05 мая 2019

Я пытаюсь получить следующую строку в правильный тип ADT:

res6: String = {"raw":"Hello","status":{"MsgSuccess":{}}} 

и использование библиотеки circe .

Тип ADT выглядит следующим образом:

sealed trait MsgDoc {
}

final case class MsgPreFailure(raw: String, reasons: Chain[String]) extends MsgDoc

final case class MsgProceed(raw: String, status: MsgStatus) extends MsgDoc

и тип MsgStatus:

sealed trait MsgStatus {

}

case object MsgSuccess extends MsgStatus

final case class MsgFailure(reasons: Chain[String]) extends MsgStatus

final case class MsgUnknown(reason: String) extends MsgStatus

и, кстати, я пытался ехать:

object MsgDocDerivation {

  import shapeless.{Coproduct, Generic}

  implicit def encodeAdtNoDiscr[A, Repr <: Coproduct](implicit
                                                      gen: Generic.Aux[A, Repr],
                                                      encodeRepr: Encoder[Repr]
                                                     ): Encoder[A] = encodeRepr.contramap(gen.to)

  implicit def decodeAdtNoDiscr[A, Repr <: Coproduct](implicit
                                                      gen: Generic.Aux[A, Repr],
                                                      decodeRepr: Decoder[Repr]
                                                     ): Decoder[A] = decodeRepr.map(gen.from)
}

и исполнение:

object Main extends App {


  val json = MsgProceed("Hello", MsgSuccess).asJson
  println(json)
  val adt = decode[MsgDoc](json.noSpaces)
  println(adt)

} 

в результате я получил:

{
  "raw" : "Hello",
  "status" : {
    "MsgSuccess" : {

    }
  }
}
Left(DecodingFailure(CNil, List())) 

Как видите, он не decode правильно.

Исходный код можно найти https://gitlab.com/playscala/adtjson.

Ответы [ 2 ]

7 голосов
/ 05 мая 2019

Я не совсем уверен, для чего предназначен материал MsgDocDerivation - он кажется ненужным и отвлекающим - но я думаю, что ключевая проблема заключается в том, что кодирование (и декодирование) Circe обусловлено статическими типами, а не классом времени выполнения кодируемого (или декодируемого) значения. Это означает, что следующие два значения JSON будут разными:

val value = MsgProceed("Hello", MsgSuccess)

val json1 = value.asJson
val json2 = (value: MsgDoc).asJson

В твоем случае у меня работает нормально:

import cats.data.Chain

sealed trait MsgStatus
case object MsgSuccess extends MsgStatus
final case class MsgFailure(reasons: Chain[String]) extends MsgStatus
final case class MsgUnknown(reason: String) extends MsgStatus

sealed trait MsgDoc
final case class MsgPreFailure(raw: String, reasons: Chain[String]) extends MsgDoc
final case class MsgProceed(raw: String, status: MsgStatus) extends MsgDoc

import io.circe.generic.auto._, io.circe.jawn.decode, io.circe.syntax._

val value: MsgDoc = MsgProceed("Hello", MsgSuccess)
val json = value.asJson

val backToValue = decode[MsgDoc](json.noSpaces)

Обратите внимание, что json отличается от того, что вы видели:

scala> json
res0: io.circe.Json =
{
  "MsgProceed" : {
    "raw" : "Hello",
    "status" : {
      "MsgSuccess" : {

      }
    }
  }
}

scala> backToValue
res1: Either[io.circe.Error,MsgDoc] = Right(MsgProceed(Hello,MsgSuccess))

Это потому, что я выполнил (типобезопасное) повышение с MsgProceed до MsgDoc. Как правило, именно так вы и работаете с ADT - вы не передаете значения, статически типизированные как подтипы класса case, а скорее как базовый тип sealed trait.

3 голосов
/ 05 мая 2019

Дело в том, что MsgProceed типа MsgProceed и типа MsgDoc кодируется для различных jsons. Таким образом, вы пытаетесь декодировать MsgDoc из неправильного JSON.

  val json = MsgProceed("Hello", MsgSuccess).asJson
  println(json)
  //{
  //  "raw" : "Hello",
  //  "status" : {
  //    "MsgSuccess" : {
  //
  //    }
  //  }
  //}
  val json1 = (MsgProceed("Hello", MsgSuccess): MsgDoc).asJson
  println(json1)
  //{
  //  "MsgProceed" : {
  //    "raw" : "Hello",
  //    "status" : {
  //      "MsgSuccess" : {
  //
  //      }
  //    }
  //  }
  //}
  val adt0 = decode[MsgProceed](json.noSpaces)
  println(adt0)
  //Right(MsgProceed(Hello,MsgSuccess))
  val adt1 = decode[MsgDoc](json1.noSpaces)
  println(adt1)
  //Right(MsgProceed(Hello,MsgSuccess))
  val adt = decode[MsgDoc](json.noSpaces)
  println(adt)
  //Left(DecodingFailure(CNil, List()))
...