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

发布时间:2020-07-07 17:06

在这里盘旋菜鸟。我正在尝试使用circe解码Scala中的case类的JSON字符串。我希望将输入JSON中的嵌套字段之一解码为Map [String,String],而不是为其创建单独的case类。

示例代码:

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)))

我在这里找到了有关如何将JSON字符串解码为地图的有趣链接:Convert Json to a Map[String, String]

但是我对如何去做没什么运气。

如果有人可以指出正确的方向或帮助我,那将很棒。

回答1

让我们分析错误:

DecodingFailure(String, List(DownField(geotile_north_lat), DownField(attributes), DownArray, DownField(events)))

这意味着我们应该在“事件”中查找名为“属性”的数组,并在其中查找名为“ geotile_north_lat”的字段。最后的错误是该字段不能作为字符串读取。确实,在您提供的有效负载中,该字段不是字符串,而是双精度型。

因此,您的问题与Map解码无关。只需使用Map [String,Double],它应该可以工作。

回答2

因此您可以执行以下操作:

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 类及其 Decoder ,因此其值为 Doubles Jsons 。< / p>