-2

我正在使用 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。

谢谢

4

3 回答 3

1

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

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
  }
}

(你可以看到它在这里工作,我离开了http4s部分,因为模拟起来很棘手,但没关系)

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

于 2020-07-18T18:18:13.367 回答
0

欢迎!我可能会从里面开始,然后走出去:

因此,对于数组中的元素data,可能类似于(我猜是域):

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

使用 Circe 的自动推导应该可以很好地处理这个问题。

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


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")
}

希望上面的代码片段返回一些有用的东西,比如 Either[E, A] 等。

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

于 2020-07-17T16:33:16.617 回答
0

你需要:

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

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

于 2020-07-17T16:31:54.680 回答