解析Json时如何解决Circe中的递归解码?
How to resolve recursive decoding in Circe when parsing Json?
我想使用 Circa 解析 JSON 字符串。您可以在下面找到输入 JSON 的示例。
这是一种递归数据。所以我的 属性 entity
包含实体的依赖关系。
我想解析依赖关系以映射 Map[String, Tasks]
。
{
"entity": [
{
"task_id": "X",
"type": "test",
"attributes": {
"name": "A",
"random_property_count": 1 // should be ignored
},
"dependencies": {
"random_name_1": {
"entity": [
{
"task_id": "907544AF",
"type": "test",
"attributes": {
"name": "B",
"random_attribute": "*"
},
"dependencies": {
"random_name_2": {
"entity": [
{
"task_id": "5",
"random_prop": "...", // should be ignored as it's not present in model
"type": "test",
"attributes": {
"name": "C"
}
}
]
}
}
}
]
}
}
}
]
}
这是我的代码:
case class Tasks (entity: Seq[Task])
case class Task(task_id: String, `type`: String, attributes: Attributes, dependencies: Map[String, Tasks])
case class Attributes(name: String)
implicit val decodeTask: Decoder[Task] = deriveDecoder[Task]
implicit val decodeTasks: Decoder[Tasks] = deriveDecoder[Tasks]
implicit val decodeAttributes: Decoder[Attributes] = deriveDecoder[Attributes]
val json = fromInputStream(getClass.getResourceAsStream("/json/example.json")).getLines.mkString
val tasks = decode[Tasks](json)
tasks match {
case Left(failure) => println(failure)
case Right(json) => println(json)
}
当我尝试将 JSON 字符串解析为我的模型时,出现如下错误:
DecodingFailure(Attempt to decode value on failed cursor, List(DownField(dependencies), DownArray, DownField(entity), DownField(random_name_2), DownField(dependencies), DownArray, DownField(entity), DownField(random_name_1), DownField(dependencies), DownArray, DownField(entity)))
可能是什么问题?
DecodingFailure
的第二个成员在这种情况下很有用,因为它提供了失败之前成功操作的历史记录以及失败操作本身(按时间倒序排列,最近的优先)。您可以像这样打印历史记录(或者只是在 DecodingFailure
的字符串表示中检查它):
scala> import io.circe.DecodingFailure
import io.circe.DecodingFailure
scala> io.circe.jawn.decode[Tasks](doc) match {
| case Left(DecodingFailure(_, history)) => history.reverse.foreach(println)
| }
DownField(entity)
DownArray
DownField(dependencies)
DownField(random_name_1)
DownField(entity)
DownArray
DownField(dependencies)
DownField(random_name_2)
DownField(entity)
DownArray
DownField(dependencies)
如果您按照这些步骤进入文档直到最后一个步骤,您将得到以下对象:
{
"task_id": "5",
"random_prop": "...",
"type": "test",
"attributes": {
"name": "C"
}
}
最后一步失败了,它是 DownField(dependencies)
,这是有道理的,因为该对象没有 dependencies
字段。
有几种方法可以解决此问题。第一个是更改 JSON 表示,以便 entity
数组中的每个对象都有一个 dependencies
字段,即使它只是 "dependencies": {}
。如果您不想或不能更改您的 JSON,您可以将 dependencies
成员设为 Option[Map[String, Tasks]]
(我刚刚确认这特别适用于您的情况)。您还可以定义自定义 Map
解码器,将缺失的字段解码为空映射,但这是一种更具侵入性的方法,我个人不推荐。
我想使用 Circa 解析 JSON 字符串。您可以在下面找到输入 JSON 的示例。
这是一种递归数据。所以我的 属性 entity
包含实体的依赖关系。
我想解析依赖关系以映射 Map[String, Tasks]
。
{
"entity": [
{
"task_id": "X",
"type": "test",
"attributes": {
"name": "A",
"random_property_count": 1 // should be ignored
},
"dependencies": {
"random_name_1": {
"entity": [
{
"task_id": "907544AF",
"type": "test",
"attributes": {
"name": "B",
"random_attribute": "*"
},
"dependencies": {
"random_name_2": {
"entity": [
{
"task_id": "5",
"random_prop": "...", // should be ignored as it's not present in model
"type": "test",
"attributes": {
"name": "C"
}
}
]
}
}
}
]
}
}
}
]
}
这是我的代码:
case class Tasks (entity: Seq[Task])
case class Task(task_id: String, `type`: String, attributes: Attributes, dependencies: Map[String, Tasks])
case class Attributes(name: String)
implicit val decodeTask: Decoder[Task] = deriveDecoder[Task]
implicit val decodeTasks: Decoder[Tasks] = deriveDecoder[Tasks]
implicit val decodeAttributes: Decoder[Attributes] = deriveDecoder[Attributes]
val json = fromInputStream(getClass.getResourceAsStream("/json/example.json")).getLines.mkString
val tasks = decode[Tasks](json)
tasks match {
case Left(failure) => println(failure)
case Right(json) => println(json)
}
当我尝试将 JSON 字符串解析为我的模型时,出现如下错误:
DecodingFailure(Attempt to decode value on failed cursor, List(DownField(dependencies), DownArray, DownField(entity), DownField(random_name_2), DownField(dependencies), DownArray, DownField(entity), DownField(random_name_1), DownField(dependencies), DownArray, DownField(entity)))
可能是什么问题?
DecodingFailure
的第二个成员在这种情况下很有用,因为它提供了失败之前成功操作的历史记录以及失败操作本身(按时间倒序排列,最近的优先)。您可以像这样打印历史记录(或者只是在 DecodingFailure
的字符串表示中检查它):
scala> import io.circe.DecodingFailure
import io.circe.DecodingFailure
scala> io.circe.jawn.decode[Tasks](doc) match {
| case Left(DecodingFailure(_, history)) => history.reverse.foreach(println)
| }
DownField(entity)
DownArray
DownField(dependencies)
DownField(random_name_1)
DownField(entity)
DownArray
DownField(dependencies)
DownField(random_name_2)
DownField(entity)
DownArray
DownField(dependencies)
如果您按照这些步骤进入文档直到最后一个步骤,您将得到以下对象:
{
"task_id": "5",
"random_prop": "...",
"type": "test",
"attributes": {
"name": "C"
}
}
最后一步失败了,它是 DownField(dependencies)
,这是有道理的,因为该对象没有 dependencies
字段。
有几种方法可以解决此问题。第一个是更改 JSON 表示,以便 entity
数组中的每个对象都有一个 dependencies
字段,即使它只是 "dependencies": {}
。如果您不想或不能更改您的 JSON,您可以将 dependencies
成员设为 Option[Map[String, Tasks]]
(我刚刚确认这特别适用于您的情况)。您还可以定义自定义 Map
解码器,将缺失的字段解码为空映射,但这是一种更具侵入性的方法,我个人不推荐。