如何使用 Circe 解码包含 json 的数组

How to decode array containing json with Circe

我有我的 circe 解码器,如下所示。我相信我的 Sentiment Decoder 工作正常,所以不会在下面包含它。

case class CryptoData(value: String, valueClassification: Sentiment)
  implicit val decoder: Decoder[CryptoData] = Decoder.instance { json =>
    for {
      value               <- json.downField("data").get[String]("value")
      valueClassification <- json.downField("data").get[Sentiment]("value_classification")
    } yield CryptoData(value, valueClassification)
  }

我的Json看起来像这样

{
  "name" : "Fear and Greed Index",
  "data" : [
    {
      "value" : "31",
      "value_classification" : "Fear",
      "timestamp" : "1631318400",
      "time_until_update" : "54330"
    }
  ],
  "metadata" : {
    "error" : null
  }
}

我只想要 valuevalue_classification。可以看出,这些值位于一个数组中。

我怀疑 Circe 正在寻找解码 List[data] 但我不想创建 case class DataInfo(list: List[Data]) 它只是感觉不对。

您刚刚错过了将 data 解析为对象数组的 downArray 调用。工作解码器:

implicit val cryptoDecoder: Decoder[CryptoData] = Decoder.instance { json =>
  val data = json.downField("data").downArray
  for {
    value <- data.get[String]("value")
    valueClassification <- data.get[Sentiment]("value_classification")
  } yield CryptoData(value, valueClassification)
}

小建议:

我建议您为 CryptoData 定义一个基本解码器,它应该只从 data 对象解码 CryptoData

{
    "value" : "31",
    "value_classification" : "Fear",
    "timestamp" : "1631318400",
    "time_until_update" : "54330"
}

至:

CryptoData("31", Fear)

如果您有一些扩展 JSON,您可以使用一些自定义解析器和解析对象向下移动到实际的 CryptoData 字段。

完整代码:

import io.circe
import io.circe.Decoder
import io.circe.parser._

trait Sentiment

object Sentiment {
  case object Fear extends Sentiment

  implicit val sentimentDecoder: Decoder[Sentiment] = Decoder.decodeString.map {
    case "Fear" => Fear
  }
}

case class CryptoData(value: String, valueClassification: Sentiment)

object CryptoData {
  implicit val cryptoDecoder: Decoder[CryptoData] = Decoder.instance { json =>
    for {
      value <- json.downField("value").as[String]
      valueClassification <- json.downField("value_classification").as[Sentiment]
    } yield CryptoData(value, valueClassification)
  }

  def decodeRaw(extendedObject: String): Either[circe.Error, Array[CryptoData]] =
    parse(extendedObject).flatMap(json => json.hcursor.downField("data").as[Array[CryptoData]])
}

测试:

val extendedJson =
  """
    |{
    |  "name" : "Fear and Greed Index",
    |  "data" : [
    |    {
    |      "value" : "31",
    |      "value_classification" : "Fear",
    |      "timestamp" : "1631318400",
    |      "time_until_update" : "54330"
    |    }
    |  ],
    |  "metadata" : {
    |    "error" : null
    |  }
    |}
    |""".stripMargin

// here should be Array
val result: Either[circe.Error, Array[CryptoData]] = CryptoData.decodeRaw(extendedJson)
// Right(CryptoData(31,Fear))
println(result.map(_.mkString(", ")))


val cryptoDataJson =
  """
    |{
    |      "value" : "31",
    |      "value_classification" : "Fear",
    |      "timestamp" : "1631318400",
    |      "time_until_update" : "54330"
    |    }
    |""".stripMargin

// Right(CryptoData(31,Fear))
println(decode[CryptoData](cryptoDataJson))