Как декодировать массив, содержащий json, с помощью 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
  }
}

Я просто хочу value а также value_classification. Как видно, эти значения находятся в массиве.

Я подозреваю, что Цирцея хочет расшифровать List[data] но я не хочу создавать case class DataInfo(list: List[Data]) это просто не кажется правильным.

1 ответ

Вы только что пропустили 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)
}

Небольшая рекомендация:

Я бы посоветовал вам определить базовый декодер, он должен просто декодировать из data объект:

      {
    "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))
Другие вопросы по тегам