Как использовать Circe для декодирования JSON списков с различными типами данных в Scala - PullRequest
1 голос
/ 23 января 2020

Есть сложный json объект. Поле «значение» в декодировании может иметь разные типы. Я новичок в scala, поэтому я буду благодарен за любую идею, чтобы сделать это.

{"price":0, 

"price_currency":"USD", 

"decode":[ 

   {"label":"Make","value":"BMW"}, 

   {"label":"Model Year","value":2017}, 

   {"label": "Equipment", "value": [ "Liftgate", "Packages"] }

   {"label": "Price", "value": [

        {"date": "2015-06-10", "price": 20500,"currency": "EUR"},
        { "date": "2015-09-21", "price": 20000,"currency": "EUR"}
    ]}

]}

поле «значение» имеет различные типы: Double, String, Seq [String], объект, который я пытался создать общую черту для этих типов и пользовательский декодер, но оказалось,

ОБНОВЛЕНИЕ привет, я хочу поделиться решением для такой структуры, можно было сделать неявное

case class VinInfo (price: Double,
                    price_currency: String,
                    decode: Seq[VinDecodeDetails])

sealed trait VinDecodeDetails

case class VinDecodeString(label: String, value: String) extends VinDecodeDetails

case class VinDecodeEquipment(label: String, value: Seq[String]) extends VinDecodeDetails

case class VinDecodePrice(label: String, value: Seq[VinPrice]) extends VinDecodeDetails

case class VinPrice(date: String, price: Double, currency: String)

object VinDecodeDetails
  implicit val eitherDoubleOrString: Decoder[Either[Double, String]] =
    Decoder[Double].map(Left(_)).or(Decoder[String].map(Right(_)))

  implicit val decodeVinInfo: Decoder[VinInfo] = new Decoder[VinInfo] {
    final def apply(c: HCursor): Decoder.Result[VinInfo] =
      for {
        price <- c.downField("price").as[Double]
        price_currency <- c.downField("price_currency").as[String]
        decode <- c.downField("decode").as[Seq[VinDecodeDetails]]
      } yield {
        new VinInfo(price, price_currency, decode)
      }
  }

  implicit val decodeVinDecodeDetails: Decoder[VinDecodeDetails] = new Decoder[VinDecodeDetails] {
    final def apply(c: HCursor): Decoder.Result[VinDecodeDetails] =
      for {
        decodeLabel <- c.downField("label").as[String]
        value = c.downField("value")
        vinDecodeDetails <- decodeLabel match {
          case "Equipment" => value.as[Seq[String]]
          case "Price"     => value.as[Seq[VinPrice]]
          case _ => value.as[Either[Double, String]].map{
            case Right(s) => s.toString
            case Left(d) => d.toString
          }
        }
      } yield {
        decodeLabel match {
          case "Equipment" => VinDecodeEquipment("Equipment", vinDecodeDetails.asInstanceOf[Seq[String]])
          case "Price"     => VinDecodePrice("Price", vinDecodeDetails.asInstanceOf[Seq[VinPrice]])
          case _ => VinDecodeString(decodeLabel, vinDecodeDetails.asInstanceOf[String])
        }
      }
  }
...