如何处理不同的 JSON 模式并分派下摆以由正确的解析器处理?
How to handle different JSON schemas and dispatch hem to be handled by the right parser?
我目前正在用 Scala 构建一个非常简单的 JSON 解析器,它必须处理两个(稍微)不同的模式。我的 objective 是解析 json 中的一个值,并基于该值,我想将其分派给相关的解码器。我在我的实现中使用了 circe,但也欢迎其他实现 and/or 建议。
我已经为我的示例制定了一个简化版本以帮助澄清问题。
我可以收到两种类型的 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 的“类型”部分的解析器,然后将数据发送给相关解析器(报价或股票)处理。但是,我想知道什么是:
- 执行此操作的有效方法
- Idiomatic/clean 方法
为了在需要时帮助重新表述我的问题,什么是处理略有不同的 JSON 模式的正确且有效的方法,并将它们转发给正确的解析器处理。
我经常在需要序列化 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
}
我目前正在用 Scala 构建一个非常简单的 JSON 解析器,它必须处理两个(稍微)不同的模式。我的 objective 是解析 json 中的一个值,并基于该值,我想将其分派给相关的解码器。我在我的实现中使用了 circe,但也欢迎其他实现 and/or 建议。
我已经为我的示例制定了一个简化版本以帮助澄清问题。
我可以收到两种类型的 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 的“类型”部分的解析器,然后将数据发送给相关解析器(报价或股票)处理。但是,我想知道什么是:
- 执行此操作的有效方法
- Idiomatic/clean 方法
为了在需要时帮助重新表述我的问题,什么是处理略有不同的 JSON 模式的正确且有效的方法,并将它们转发给正确的解析器处理。
我经常在需要序列化 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
}