使用 Circe 将包含 HList 的案例类解析为 JSON 字符串

Posted

技术标签:

【中文标题】使用 Circe 将包含 HList 的案例类解析为 JSON 字符串【英文标题】:Parse a case class containing an HList into a JSON string, using Circe 【发布时间】:2017-04-12 09:42:52 【问题描述】:

我在 Scala 中做一件事。我有以下案例类:

import shapeless._
case class Foo(param1: String, param2: HList)

我想获得这种类型的 JSON 表示使用Circe。我还想将生成的 JSON 字符串映射回类型。

circe-shapes 模块会自动推导 HList,从 HList 到 JSON 再返回很容易。看这个例子:

scala> import shapeless._
import shapeless._

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

scala> import io.circe.shapes._
import io.circe.shapes._

scala> val myList = 30 :: "car" :: HNil
myList: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 30 :: car :: HNil

scala> val listJson = myList.asJson
listJson: io.circe.Json =
[
  30,
  "car"
]

scala> listJson.as[HList] // won't work
<console>:32: error: could not find implicit value for parameter d: io.circe.Decoder[shapeless.HList]
       listJson.as[HList]
                  ^

scala> listJson.as[::[Int, ::[String, HNil]]]
res3: io.circe.Decoder.Result[shapeless.::[Int,shapeless.::[String,shapeless.HNil]]] = Right(30 :: car :: HNil)

包含“标准”类型的案例类也很简单:

scala> case class Bar(one: String, a: Double, andAn: Int)
defined class Bar

scala> val myBar = Bar("pie", 4.35, 2)
myBar: Bar = Bar(pie,4.35,2)

scala> val barJson = myBar.asJson
barJson: io.circe.Json =

  "one" : "pie",
  "a" : 4.35,
  "andAn" : 2


scala> barJson.as[Bar]
res5: io.circe.Decoder.Result[Bar] = Right(Bar(pie,4.35,2))

明确 HList 的类型可以创造奇迹,但这有点违背 HList 的目的:

scala> case class Foo2(a: String, b: ::[Int, ::[String, HNil]])
defined class Foo2

scala> val myFoo2 = Foo2("ark", 42 :: "meg" :: HNil)
myFoo2: Foo2 = Foo2(ark,42 :: meg :: HNil)

scala> val foo2Json = myFoo2.asJson
foo2Json: io.circe.Json =

  "a" : "ark",
  "b" : [
    42,
    "meg"
  ]


scala> foo2Json.as[Foo2]
res8: io.circe.Decoder.Result[Foo2] = Right(Foo2(ark,42 :: meg :: HNil))

Circe 可以解码任意 HList 吗?

【问题讨论】:

【参考方案1】:

是的,circe 可以做到这一点,但您需要更改您的案例类以使其保留有关 HList 的更多信息:

import shapeless._

case class Foo[L <: HList](param1: String, param2: L)

然后是导入:

import io.circe.generic.auto._, io.circe.shapes._, io.circe.parser._, io.circe.syntax._

然后你可以拨打asJson等:

scala> val foo = Foo("ark", 42 :: "meg" :: HNil)
foo: Foo[shapeless.::[Int,shapeless.::[String,shapeless.HNil]]] = Foo(ark,42 :: meg :: HNil)

scala> foo.asJson
res0: io.circe.Json =

  "param1" : "ark",
  "param2" : [
    42,
    "meg"
  ]


scala> decode[Foo[Int :: String :: HNil]](res0.noSpaces).right.foreach(println)
Foo(ark,42 :: meg :: HNil)

在这种情况下,具体而言,circe 中的派生机制需要有关 hlist 元素的静态信息才能生成编码器和解码器,但更一般地,当您使用 hlist 时,您会希望避免使用成员或键入为普通旧 HList 的值。

对于HList 类型的东西,您无能为力。您可以为其添加值,并且可以通过toString 获得字符串表示,但仅此而已——您不能使用任何Shapeless 的ops 类型类,您无法恢复有关各个类型的任何信息,等等。您几乎总是(可能总是)想要一个L &lt;: HList,它允许您保留使该类型有用的信息。

【讨论】:

以上是关于使用 Circe 将包含 HList 的案例类解析为 JSON 字符串的主要内容,如果未能解决你的问题,请参考以下文章

Circe - 解码/编码 json 时在案例类中使用默认字段

使用 circe 在 Scala 中 JSON 将嵌套字段解码为 Map[String, String]

如何使用 circe 将密封特征案例对象转换为字符串

Circe Scala - 编码和解码 Map[] 和案例类

使用 circe 将 Scala None 编码为 JSON 值

带有案例类的抽象类的scala circe编码器/解码器