I have my circe Decoder as shown below. I am confident my Sentiment Decoder works correctly so won't include it below.
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)
}
my Json looks like this
{
"name" : "Fear and Greed Index",
"data" : [
{
"value" : "31",
"value_classification" : "Fear",
"timestamp" : "1631318400",
"time_until_update" : "54330"
}
],
"metadata" : {
"error" : null
}
}
I simply want value
and value_classification
. As can be seen, those values sit within an array.
I suspect Circe is looking to decode a List[data]
but I don't want to create a case class DataInfo(list: List[Data])
it just doesn't feel right.
CodePudding user response:
You just missed a downArray
call to parse data
as the array of objects. Working decoder:
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)
}
Small recomendataion:
I would advise you to define a basic decoder for CryptoData
, it should just decode CryptoData
from the data
object:
{
"value" : "31",
"value_classification" : "Fear",
"timestamp" : "1631318400",
"time_until_update" : "54330"
}
to:
CryptoData("31", Fear)
and if you have some extended JSON, you can just move down to the actual CryptoData
field using some custom parser and parse object.
full code:
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]])
}
testing:
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))