首页 > 解决方案 > 如何处理不同的 JSON 模式并分派 hem 以由正确的解析器处理?

问题描述

我目前正在 Scala 中构建一个非常简单的 JSON 解析器,它必须处理两个(稍微)不同的模式。我的目标是解析 json 中的一个值,并基于该值,我想将它分派给相关的解码器。我已经使用 circe 来实现我的实现,但也欢迎其他实现和/或建议。

我为我的示例制定了一个简化版本,以帮助澄清问题。

我可以接收两种类型的 JSON,一种是股票:

"data": {
  "name": "XYZ"
},
  "type": "STOCK"
}

或报价(类似于股票,但包含价格)。

"data": {
  "name": "ABC",
  "price": 1151.6214,
},
  "type": "QUOTE"
}

在我这边,我开发了一个简单的解码器,看起来像这样(用于股票):

implicit private val dataDecoder: Decoder[Stock] = (hCursor: HCursor) => {
    for {
      isin <- hCursor.downField("data").downField("name").as[String]
      typ <- hCursor.downField("type").as[StockType]
    } yield Instrument(name, typ, LocalDateTime.now())
  }

我还可以开发一个解析器,它只解析 JSON 的“类型”部分,然后发送数据以由相关解析器(报价或股票)处理。但是,我想知道是什么:

  1. 有效的方法来做到这一点
  2. 惯用/干净的方式来做到这一点

如果需要,为了帮助重新表述我的问题,处理略有不同的 JSON 模式并将它们转发给正确的解析器处理的正确和有效的方法是什么。

标签: jsonscalacirce

解决方案


当需要序列化 ​​ADT 时,我通常会遇到这种情况。正如有人在对您的问题的评论中提到的那样,circe 支持 ADT 编解码器自动生成,但是我通常更喜欢手动编写编解码器。

无论如何,在像您这样的情况下,我会按照以下方式做一些事情:

sealed trait Data
case class StockData(name: String) extends Data
case class QuoteData(name: String, quote: Double) extends Data

implicit val stockDataEncoder: Encoder[StockData] = ???
implicit val stockDataDecoder: Decoder[StockData] = ???
implicit val quoteDataEncoder: Encoder[QuoteData] = ???
implicit val quoteDataDecoder: Decoder[QuoteData] = ???

implicit val dataEncoder: Encoder[Data] = Encoder.instance {
  case s: StockData => stockDataEncoder(s).withObject(_.add("type", "stock))
  case q: QuoteData => quoteDataEncoder(q).withObject(_.add("type", "quote"))
}

implicit val dataDecoder: Decoder[Data] = Decoder.instance { c =>
  for {
    stype <- c.get[String]("type)
    res <- stype match {
       case "stock" => stockDataDecoder(c)
       case "quote" => quoteDataDecoder(c)
       case unk => Left(DecodingFailure(s"Unsupported data type: ${unk}", c.history))
    }
  } yield res
}

推荐阅读