JSON 在 Scala 中使用 circe 将嵌套字段解码为 Map[String, String]
JSON decode nested field as Map[String, String] in Scala using circe
这里有一个菜鸟。我正在尝试使用 circe 在 Scala 中将 JSON 字符串解码为大小写 class。我希望将输入 JSON 中的嵌套字段之一解码为 Map[String, String] 而不是为其创建单独的 case class。
示例代码:
import io.circe.parser
import io.circe.generic.semiauto.deriveDecoder
case class Event(
action: String,
key: String,
attributes: Map[String, String],
session: String,
ts: Long
)
case class Parsed(
events: Seq[Event]
)
Decoder[Map[String, String]]
val jsonStr = """{
"events": [{
"ts": 1593474773,
"key": "abc",
"action": "hello",
"session": "def",
"attributes": {
"north_lat": -32.34375,
"south_lat": -33.75,
"west_long": -73.125,
"east_long": -70.3125
}
}]
}""".stripMargin
implicit val eventDecoder = deriveDecoder[Event]
implicit val payloadDecoder = deriveDecoder[Parsed]
val decodeResult = parser.decode[Parsed](jsonStr)
val res = decodeResult match {
case Right(staff) => staff
case Left(error) => error
}
我最终在属性字段上遇到解码错误,如下所示:
DecodingFailure(String, List(DownField(north_lat), DownField(attributes), DownArray, DownField(events)))
我在这里找到了一个有趣的 link,关于如何将 JSON 字符串解码为地图:
但我对如何去做这件事不太走运。
如果有人能为我指出正确的方向或帮助我解决这个问题,那就太棒了。
让我们分析错误:
DecodingFailure(String, List(DownField(geotile_north_lat), DownField(attributes), DownArray, DownField(events)))
这意味着我们应该在“事件”中查找名为“属性”的数组,并在该数组中查找名为“geotile_north_lat”的字段。最后一个错误是该字段无法作为字符串读取。事实上,在您提供的有效负载中,该字段不是字符串,而是双精度。
所以你的问题与地图解码无关。只需使用 Map[String, Double] 就可以了。
所以你可以这样做:
final case class Attribute(
key: String,
value: String
)
object Attribute {
implicit val attributesDecoder: Decoder[List[Attribute]] =
Decoder.instance { cursor =>
cursor
.value
.asObject
.toRight(
left = DecodingFailure(
message = "The attributes field was not an object",
ops = cursor.history
)
).map { obj =>
obj.toList.map {
case (key, value) =>
Attribute(key, value.toString)
}
}
}
}
final case class Event(
action: String,
key: String,
attributes: List[Attribute],
session: String,
ts: Long
)
object Event {
implicit val eventDecoder: Decoder[Event] = deriveDecoder
}
你可以这样使用:
val result = for {
json <- parser.parse(jsonStr).left.map(_.toString)
obj <- json.asObject.toRight(left = "The input json was not an object")
eventsRaw <- obj("events").toRight(left = "The input json did not have the events field")
events <- eventsRaw.as[List[Event]].left.map(_.toString)
} yield events
// result: Either[String, List[Event]] = Right(
// List(Event("hello", "abc", List(Attribute("north_lat", "-32.34375"), Attribute("south_lat", "-33.75"), Attribute("west_long", "-73.125"), Attribute("east_long", "-70.3125")), "def", 1593474773L))
// )
您可以自定义 Attribute class 及其 Decoder,因此它们的值为 Doubles 或 Jsons.
这里有一个菜鸟。我正在尝试使用 circe 在 Scala 中将 JSON 字符串解码为大小写 class。我希望将输入 JSON 中的嵌套字段之一解码为 Map[String, String] 而不是为其创建单独的 case class。
示例代码:
import io.circe.parser
import io.circe.generic.semiauto.deriveDecoder
case class Event(
action: String,
key: String,
attributes: Map[String, String],
session: String,
ts: Long
)
case class Parsed(
events: Seq[Event]
)
Decoder[Map[String, String]]
val jsonStr = """{
"events": [{
"ts": 1593474773,
"key": "abc",
"action": "hello",
"session": "def",
"attributes": {
"north_lat": -32.34375,
"south_lat": -33.75,
"west_long": -73.125,
"east_long": -70.3125
}
}]
}""".stripMargin
implicit val eventDecoder = deriveDecoder[Event]
implicit val payloadDecoder = deriveDecoder[Parsed]
val decodeResult = parser.decode[Parsed](jsonStr)
val res = decodeResult match {
case Right(staff) => staff
case Left(error) => error
}
我最终在属性字段上遇到解码错误,如下所示:
DecodingFailure(String, List(DownField(north_lat), DownField(attributes), DownArray, DownField(events)))
我在这里找到了一个有趣的 link,关于如何将 JSON 字符串解码为地图:
但我对如何去做这件事不太走运。
如果有人能为我指出正确的方向或帮助我解决这个问题,那就太棒了。
让我们分析错误:
DecodingFailure(String, List(DownField(geotile_north_lat), DownField(attributes), DownArray, DownField(events)))
这意味着我们应该在“事件”中查找名为“属性”的数组,并在该数组中查找名为“geotile_north_lat”的字段。最后一个错误是该字段无法作为字符串读取。事实上,在您提供的有效负载中,该字段不是字符串,而是双精度。
所以你的问题与地图解码无关。只需使用 Map[String, Double] 就可以了。
所以你可以这样做:
final case class Attribute(
key: String,
value: String
)
object Attribute {
implicit val attributesDecoder: Decoder[List[Attribute]] =
Decoder.instance { cursor =>
cursor
.value
.asObject
.toRight(
left = DecodingFailure(
message = "The attributes field was not an object",
ops = cursor.history
)
).map { obj =>
obj.toList.map {
case (key, value) =>
Attribute(key, value.toString)
}
}
}
}
final case class Event(
action: String,
key: String,
attributes: List[Attribute],
session: String,
ts: Long
)
object Event {
implicit val eventDecoder: Decoder[Event] = deriveDecoder
}
你可以这样使用:
val result = for {
json <- parser.parse(jsonStr).left.map(_.toString)
obj <- json.asObject.toRight(left = "The input json was not an object")
eventsRaw <- obj("events").toRight(left = "The input json did not have the events field")
events <- eventsRaw.as[List[Event]].left.map(_.toString)
} yield events
// result: Either[String, List[Event]] = Right(
// List(Event("hello", "abc", List(Attribute("north_lat", "-32.34375"), Attribute("south_lat", "-33.75"), Attribute("west_long", "-73.125"), Attribute("east_long", "-70.3125")), "def", 1593474773L))
// )
您可以自定义 Attribute class 及其 Decoder,因此它们的值为 Doubles 或 Jsons.