1
votes

Hi the Decode Person example in the documentation is great if the JSON has a key and value and you can use the key name to extract its value, but what about if the string that makes up the key is arbitrary but meaningful.

for Fxample one open cryptocurrency api can give historic prices of coins and the structure of the JSON returned is different depending on the base currency of the coin I'm asking for and the various quote currencies I want it priced in.. for example lets say I want the price at a particular date of 'DOGE' in 'AUD' and 'XRP' the returned JSON looks like

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

I can't navigate to base and get its value and then prices and get them as the JSON is not stuctured that way, I need to look for 'DOGE' as a Key then in the Object retrned know that there will be a 'AUD' key and 'XRP' key. And of course that will be different for every result depending on my query.

Of course I know these keys as I create the search based on them but how can I use Argonaut to parse this JSON? Can I somehow create a Decode that closes over my key names?

Any help or guidance is appreciated, thanks.

2

2 Answers

1
votes

Since you don't know what the property names are going to be ahead of time, you can't create a codec and decode the raw JSON directly to a Scala class.

You want to parse the raw JSON as a generic argonaut.Json object, then you can pattern match or use fold to examine the contents. For example:

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

You can see the methods available on argonaut's Json object by inspecting the source code.

1
votes

As per Fried Brice's answer I did go down the parse route then mapped the resulting Either to produce my data type see code snippet below, suggestions, improvements welcome.

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)])