Modelling API Responses With sbt-json – Print Current Bitcoin Price

I’m currently working on an sbt plugin that generates Scala case classes at compile time to model JSON API responses for easy deserialization especially with the Scala play-json library.

The plugin makes it possible to access JSON documents in a statically typed way including auto-completion. It takes a sample JSON document as input (either from a file or a URL) and generates Scala types that can be used to read data with the same structure.

Let’s look at a basic example, an app that prints the current Bitcoin price to the console.

Setup

Install the plugin and add the play-json library dependency.

Edit project/plugins.sbt and add the following line:

addSbtPlugin("com.github.battermann" % "sbt-json" % "0.2.4")

In the build.sbt file we have to enable the plugin by adding this line:

enablePlugins(SbtJsonPlugin)

Add the dependency to the play-json library:

libraryDependencies += "com.typesafe.play" %% "play-json" % "2.6.0"

You might want to run reload if sbt is running.

Specify JSON source

By default the plugin analyzes all files in src/main/resources/json with a .json extension and generates Scala case classes that represent the corresponding JSON schema.

It is also possible to specify URLs that serve JSON data for case class generation.

Let’s add a public URL from the coinddesk API to retrieve the current Bitcoin price to the build.sbt file:

jsonUrls += "https://api.coindesk.com/v1/bpi/currentprice.json"

Run reload and compile in sbt.

On the sbt compile task the Scala case classes are generated in the package models.json.currentprice.

Writing the app

First we have to import the play-json library and the generated JSON models:

import models.json.currentprice._
import play.api.libs.json.Json

Now we can get the API response like this:

val url = "https://api.coindesk.com/v1/bpi/currentprice.json"
val rawJson = scala.io.Source.fromURL(url).mkString

Because sbt-json generates play-json formats by default, we can parse the result to a JsValue and implicitly cast it to the generated Scala case class:

val currentPrice = Json.parse(rawJson).as[Currentprice]

Finally we can access the required fields as they are represented as plain old Scala case classes with auto-completion enabled and print the current Bitcoin price in EURO on the console:

val info = currentPrice.bpi.EUR.description
val priceInEuro = currentPrice.bpi.EUR.rate_float
val date = currentPrice.time.updated
println(s"Current Bitcoin price ($info): $priceInEuro (timestamp: $date)")

Running the program produces the following output:

Current Bitcoin price (Euro): 3691.8746 (timestamp: Aug 27, 2017 18:54:00 UTC)

Conclusion

There are a few more settings that can be used to configure the case class generation process and there are features that are not discussed here.

E.g. the generator will unify similar schemas and tries to find unique names that won’t clash with any Scala reserved words. Fore more details see the GitHub README page.

Contributions are very welcome and highly appreciated. You can contribute by sending pull request, by reporting bugs and feature requests here, or by giving feedback and suggestions for improvement.

Resources