如何使用Argonaut解码密钥名称有意义的结构不良的JSON

时间:2018-02-04 21:55:12

标签: scala argonaut

如果JSON有一个键和值,并且您可以使用键名来提取其值,那么the documentation中的Decode Person示例很棒,但是如果构成键的字符串是任意的,那么有意义。

对于Fxample,一个开放的加密货币api可以给出硬币的历史价格,并且返回的JSON的结构是不同的,这取决于我要求的硬币的基础货币以及我想要它的各种报价货币。例如,假设我想要特定日期的价格“DOGE'在' AUD'和' XRP'返回的JSON看起来像

{"DOGE":{"AUD":0.008835,"XRP":0.004988}}

我无法导航到基础并获得其价值然后价格并获得它们,因为JSON没有被这样结构化,我需要寻找“DOGE”'作为一个密钥,然后在对象中,知道会有一个' AUD'密钥和' XRP'键。当然,根据我的查询,每个结果都会有所不同。

当然我知道这些键,因为我基于它们创建搜索但是如何使用Argonaut来解析这个JSON?我可以以某种方式创建一个关闭我的密钥名称的解码吗?

感谢任何帮助或指导,谢谢。

2 个答案:

答案 0 :(得分:1)

由于您不知道属性名称将提前知道什么,因此您无法创建编解码器并将原始JSON直接解码为Scala类。

您希望将原始JSON解析为通用argonaut.Json对象,然后您可以模式匹配或使用fold来检查内容。例如:

val rawJson: String = ...
val parsed: Either[String, argonaut.Json] = argonaut.Parse.parse(rawJson)

您可以通过检查source code来查看argonaut Json对象上可用的方法。

答案 1 :(得分:1)

根据Fried Brice的回答,我确实沿着解析路线向下映射了生成的Either以生成我的数据类型,请参阅下面的代码片段,建议,改进欢迎。

def parseHistoricPriceJSON(rawJson: String, fromcurrency: Currency, toCurrencies: List[Currency]): Either[String, PricedAsset] = {
import argonaut._, Argonaut._
import monocle.macros.syntax.lens._
val parsed: Either[String, Json] = Parse.parse(rawJson)
val myTocurrs = Currency("XRP") :: toCurrencies

parsed.right.map(outer => {
  val cursor = outer.cursor
  val ps = for {
    toC <- myTocurrs
    prices <- cursor.downField(fromcurrency.sym)
    price <- prices.downField(toC.sym)
    thep <- price.focus.number
  } yield (toC, thep.toDouble.get)

  PricedAsset(fromcurrency, ps)
})

}

case class Currency(sym: String) extends AnyVal {
  def show = sym
}

case class PricedAsset(base:Currency, quotePrices: List[(Currency,Double)])