json 的实体解码 return 任何值的 list/seq

entity decode for json that return a list/seq of any val

我正在使用 http4s 构建应用程序的后端。在应用程序中,我收到来自外部 api(不是我正在处理的那个)的 json 响应。 api 响应是下面这种模式。

json 回复:

     `{
"datatable" : {
             "data" : [["AAPl", "MRT", "2020-03-20", 123, 123, 12.4, 233, 3234], 
                      ["AAPl", "MRT", "2020-03-20", 123, 123, 12.4, 233, 3234]],
              "meta": {
                      "next_date : null
              }

}`

我的问题是? 有人可以告诉我如何创建一个实体解码器和实体编码器来解码模式。我似乎可以让它工作。

目前我有:

  object Stocks {

   case class tickerInfo(ticker: String, dim:String, date: String, a: Int, b: Int, c: Float, d: Int, e: Int)

   case class data(data: Seq[tickerInfo])

   case class meta(next_date : Option[String])

   case class table(data: data, meta:meta)

   case class stockInfo(datatable:table)


  object stockInfo {
    implicit def stockInfoEntityDecoder[F[_]:Sync]: EntityDecoder [F, stockInfo] = jsonOf
    implicit def stockInfoEntityEncoder[F[_] : Applicative]: EntityEncoder[F, stockInfo] = jsonEncoderOf
     }

     val decodedJson = C.expect[stockInfo](GET(Uri.uri("www.externalApi.com")
     }

但这不起作用。请有人告诉我哪里错了。

我收到 运行 时间错误 *不是编译错误)。它是一个 http4s 错误,上面写着 - InvalidMessageBodyFailure.

谢谢

您需要:

  1. 创建一个数据模型,大概由某些案例 类 和可能的密封特征组成。这个数据模型应该是什么样子取决于你正在与之交谈的外部 API。
  2. 为此数据模型创建 JSON 个解码器。这些应该放在 case 类' companion objects 中,因为这将允许编译器在需要时找到它们而无需导入任何东西。
  3. 使用 http4s-circe 库将这些解码器与 http4s 集成。 https://http4s.org/v0.19/json/

如果你做的一切都正确,那么你应该能够使用 Http4s client 来检索数据,例如。 G。 httpClient.expect[YourModelClass](Uri.uri("http://somewhere.com/something/")).

欢迎光临!我可能会从里面开始,然后自己出去:

所以对于 data 数组中的元素,可能类似于(我在域上猜测):

case class Stock(
  name: String,
  mart: String,
  datePosted: LocalDate,
  a: Int,
  b: Int,
  price: Double,
  c: Int,
  d: Int
)

使用 Circe 的 automatic derevation 应该可以很好地处理这个问题。

数据看起来像是一组分解元素的数组?因此,如果是这种情况,您可能必须编写一些提取逻辑以手动转换为内部对象。控制器中的类似内容可能会有所帮助:


elem match {
  case name :: mart :: date :: a :: b :: price :: c :: d :: Nil => Stock(name, mart, date, a, b, price, c, d)
  case invalid @ _ => log.warn(s"Invalid record: $invalid")
}

希望上面的片段 returns 像 Either[E, A] 等有用的东西

最后,您需要外部 JSON 的对象。一些易于捕获的东西,例如 case class ExternalApiRequest(dataTable: T),其中 T 是适合上述情况的类型。 List[String] 在最坏的情况下? 希望这有帮助!如果您遇到 运行

的任何具体错误,请告诉我

您的模型中有几个错误,但主要问题是 circe 无法自动将 json 数组解码为案例 class.
如果您无法修改数据源,则需要创建自己的自定义编解码器。

object Stocks {
  final case class TickerInfo(ticker: String, dim: String, date: String, a: Int, b: Int, c: Float, d: Int, e: Int)

  final case class Meta(next_date : Option[String])
  final case class Table(data: List[TickerInfo], meta: Meta)
  final case class StockInfo(datatable: Table)
  
  object StockInfo {
    implicit final val TickerInfoDecoder: Decoder[TickerInfo] = Decoder[List[Json]].emap {
      case ticker :: dim :: date :: a :: b :: c :: d :: e :: Nil =>
        (
          ticker.as[String],
          dim.as[String],
          date.as[String],
          a.as[Int],
          b.as[Int],
          c.as[Float],
          d.as[Int],
          e.as[Int]
        ).mapN(TickerInfo).left.map(_.toString)
      
      case list =>
        Left(s"Bad number of fields in: ${list.mkString("[", ", ", "]")}")
    }
    
    implicit final val MetaDecoder: Decoder[Meta] = deriveDecoder
    implicit final val TableDecoder: Decoder[Table] = deriveDecoder
    implicit final val StockInfoDecoder: Decoder[StockInfo] = deriveDecoder
  }
}

(你可以看到它工作 here,我把 http4s 部分放在外面,因为它很难模拟,但它不应该无所谓)

可以通过为每个字段提供更有用的消息来改进自定义编码器的错误报告。这留作 reader.

的练习