Json Can';在Scala中使用Circe解码子类
我有一个Scala项目,其中我使用Circe处理json。 我在将JSON解码为层次结构的子类时遇到困难 我遇到问题的代码是以下测试:Json Can';在Scala中使用Circe解码子类,json,scala,enums,traits,circe,Json,Scala,Enums,Traits,Circe,我有一个Scala项目,其中我使用Circe处理json。 我在将JSON解码为层次结构的子类时遇到困难 我遇到问题的代码是以下测试: test("FailingResponse - Conversion between case object and Json works") { val caseObject = FailingResponse("Some Error", StatusCodes.INTERNAL_ERROR) val jsonString = caseOb
test("FailingResponse - Conversion between case object and Json works") {
val caseObject = FailingResponse("Some Error", StatusCodes.INTERNAL_ERROR)
val jsonString = caseObject
.asJson
.printWith(Printer.noSpaces)
decode[ValuationResponse](jsonString) must be(Right(caseObject))
}
我希望能够解码到ValuationResponse的任何子类,因为在解码时,我无法确定响应是失败响应还是成功响应。我希望解码器能够归纳出它是什么类型的评估响应,对它进行解码,并将其作为“通用”评估响应提供。然后,我可以用一个匹配的案例或类似的东西来处理它,以获得实际的特定类型。
相反,我在那个测试中得到的是一个解码失败错误。我做错了什么
这是层次结构的代码:
sealed trait ValuationResponse {
def statusCode: StatusCode
}
case class SuccessfulResponse(values: List[StockValuation], symbol: String, function: TimeSeriesType, statusCode: StatusCode) extends ValuationResponse
case class FailingResponse(reason: String, statusCode: StatusCode) extends ValuationResponse
case class ValuationRequest(function: TimeSeriesType = TIME_SERIES_INTRADAY, symbol: String, interval: IntraDayInterval = IntraDayIntervals.MIN_5)
object derivation {
implicit val encodeResponse: Encoder[ValuationResponse] = Encoder.instance {
case response@SuccessfulResponse(_, _, _, _) => response.asJson
case response@FailingResponse(_, _) => response.asJson
}
implicit val decodeResponse: Decoder[ValuationResponse] =
List[Decoder[ValuationResponse]](
Decoder[SuccessfulResponse].widen,
Decoder[FailingResponse].widen
).reduceLeft(_ or _)
implicit val encodeRequest: Encoder[ValuationRequest] = Encoder.instance {
case response@ValuationRequest(_, _, _) => response.asJson
}
implicit val decodeRequest: Decoder[ValuationRequest] =
List[Decoder[ValuationRequest]](
Decoder[ValuationRequest].widen
).reduceLeft(_ or _)
}
这些是它使用的枚举(是的,我知道为状态代码使用枚举是愚蠢的啊哈):
当我对您的代码进行了一些修改(删除了一些东西以使代码编译更容易)后: 我得到 但是,当我从对象导入隐式时
@ import derivation._
import derivation._
@ decode[ValuationResponse](jsonString)
res23: Either[Error, ValuationResponse] = Right(FailingResponse("Some Error", 200))
问题是,默认情况下,Circe使用discrimination字段来区分sum类型的成员。如果不导入派生
对象,则可以查看值的编码格式:
@ {
val jsonString = (caseObject : ValuationResponse)
.asJson
.printWith(Printer.noSpaces)
}
jsonString: String = "{\"FailingResponse\":{\"reason\":\"Some Error\",\"statusCode\":200}}"
因此,在解码案例类时,您使用了自动派生的编解码器-如果您删除了import io.circe.generic.auto.\u
,那么当您尝试在不导入自己编写的代码的情况下解码时,您的编译将失败(import derivation.\u
)
为避免将来出现此类情况:
- 不要在编解码器使用站点上的生产上导入
-这样可以为应该使用手写/手动导出编解码器的案例类派生新的编解码器(这会导致像这样的错误)io.circe.generic.auto.\u
- 首选
在需要的地方调用派生的编解码器(而不是io.circe.generic.semiauto.\u
writeDecoder[A]
)deriveDecoder[A]
- 将半自动派生的编解码器以及手写的编解码器放在派生编解码器类型的伴生对象中(如果可能的话)-这将消除每次需要时手动导入它们的需要
@ derivation.decodeRequest.decodeJson("test".asJson)
java.lang.NullPointerException
ammonite.$sess.cmd7$.<clinit>(cmd7.sc:1)
你会得到:
@ val decodeRequests: Decoder[ValuationRequest] =
List[Decoder[ValuationRequest]](
io.circe.generic.semiauto.deriveDecoder[ValuationRequest].widen
).reduceLeft(_ or _)
decodeRequests: Decoder[ValuationRequest] = io.circe.generic.decoding.DerivedDecoder$$anon$1@30570f04
@ decodeRequests.decodeJson("test".asJson)
res9: Decoder.Result[ValuationRequest] = Left(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(test))))
好吧,在做了你建议的那些修改之后,解码工作就开始了。但在编码时,我得到:异常或错误导致运行中止。eventbus.ValuationRequest$.$anonfun$encodeRequest$1(eventBusCases.scala:36)第36行为:caseresponse@ValuationRequest(,,)=>response.asj问题现已解决。我不得不将赋值请求更改为:object ValuationRequest{implicit val encodeRequest:Encoder[ValuationRequest]=deriveEncoder[ValuationRequest]implicit val decodequest:Decoder[ValuationRequest]=deriveDecoder[ValuationRequest]}asJson的递归性显然带来了麻烦。
@ {
val jsonString = (caseObject : ValuationResponse)
.asJson
.printWith(Printer.noSpaces)
}
jsonString: String = "{\"FailingResponse\":{\"reason\":\"Some Error\",\"statusCode\":200}}"
import io.circe.generic.semiauto._
sealed trait ValuationResponse ...
object ValuationResponse {
implicit val decodeResponse: Decoder[ValuationResponse] =
List[Decoder[ValuationResponse]](
deriveDecoder[SuccessfulResponse].widen,
deriveDecoder[FailingResponse].widen
).reduceLeft(_ or _)
}
@ derivation.decodeRequest.decodeJson("test".asJson)
java.lang.NullPointerException
ammonite.$sess.cmd7$.<clinit>(cmd7.sc:1)
implicit val decodeRequest: Decoder[ValuationRequest] =
List[Decoder[ValuationRequest]](
deriveDecoder[ValuationRequest].widen
).reduceLeft(_ or _)
@ val decodeRequests: Decoder[ValuationRequest] =
List[Decoder[ValuationRequest]](
io.circe.generic.semiauto.deriveDecoder[ValuationRequest].widen
).reduceLeft(_ or _)
decodeRequests: Decoder[ValuationRequest] = io.circe.generic.decoding.DerivedDecoder$$anon$1@30570f04
@ decodeRequests.decodeJson("test".asJson)
res9: Decoder.Result[ValuationRequest] = Left(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(test))))