Circe: A Json library for Scala and Scala.Js

Reading Time: 3 minutes

Circe lets us play with json and provides rich set of methods with almost every possible functionality you need to have for json.

Now, we are interested in knowing the good things that will motivate us to use the new library Circe for handling my json objects and parsing my json strings, while we already have many other libraries for scala.

First of all, let’s go through few interesting points about Circe:

  • It lets you deal with json with having no boilerplate and no runtime reflection.
  • Circe is actually the fork of Argonaut with a few important differences.
  • If we talk a little about Argonaut library, it is the best one present yet. It is the Purely Functional JSON in scala. If we have situation to deal with JSON in scala, we should surely be using Argonaut.
  • Circe uses cats instead of scalaz and uses shapeless for codec

In order to use Circe, simply add the following dependencies in the build.sbt of your project.

libraryDependencies ++= Seq(
"io.circe" % "circe-core_2.11" % "0.8.0",
"io.circe" %% "circe-parser" % "0.8.0",
"io.circe" %% "circe-generic" % "0.8.0"
)

We know that, Json data are represented in the key/value pairs separated by “:”, where key is the name of the property which should be a string and the value can be of any types.

1. To support the different data types in the value field of the Json, Circe has the multiple methods available to create the Json object. We can try a few of them in Scala REPL,

scala> import io.circe.Json
import io.circe.Json

scala> val jsonFromString = Json.fromString("My String json")
jsonFromString: io.circe.Json = "My String json"

scala> val jsonFromInt = Json.fromInt(4)
jsonFromInt: io.circe.Json = 4

2. If you, we have to create json with fields having key value pair we have methods fromFields and fromValues. The definition of these methods is as:

final def fromFields(fields: Iterable[(String, io.circe.Json)]): io.circe.Json
final def fromValues(values: Iterable[io.circe.Json]): io.circe.Json

Now, try using these methods to generate json,

scala> val jsonFromFields = Json.fromFields(List(("stringKey", Json.fromString("stringValue")),("intKey",Json.fromInt(4))))
jsonFromFields: io.circe.Json =
{
 "stringKey" : "stringValue",
 "intKey" : 4
}

scala> val jsonFromValues = Json.fromValues(List(Json.fromString("stringValue"), Json.fromInt(1)))
jsonFromValues: io.circe.Json =
[
 "stringValue",
 1
]

3. To get the first json object present in the json, it has init method,

scala> jsonFromValues mapArray (json => json.init)
res0: io.circe.Json =
[
 "stringValue"
]

Parsing  the Json

For this, you have to import io.circe.parser._, this is not available in circe.core

scala> import io.circe.parser._
import io.circe.parser._

scala> parse("""{"key":1}""")
res1: Either[io.circe.ParsingFailure,io.circe.Json] =
Right({
 "key" : 1
})

Now, this is something I found nice here. Parsing a json string is always subjected to risk of failure, so the parse function always returns Either which can handle the failure case as well.

Traversing the Json

Traversing the Json fields using the circe library is quite easy. We need to create HCursor with focus on the root of the Json Document. After that, we can move to each field and extract data from it.

scala> val cursor = jsonFromFields.hcursor
cursor: io.circe.HCursor = io.circe.cursor.TopCursor@5f4e473a

scala> val intKey = cursor.downField("intKey").as[Int]
intKey: io.circe.Decoder.Result[Int] = Right(4)

scala> val intKeySecond = cursor.get[Int]("intKey")
intKeySecond: io.circe.Decoder.Result[Int] = Right(4)

Encoding and Decoding the Case classes

It is possible to derive Encoders and Decoders for many types with no boilerplate at all using circe. circe uses shapeless to automatically derive the necessary type class instances.

The mapping of Json to the case class or vice-versa is just a one step process. You have to import the io.circle.syntax._ for access to asJson method  and io.circe.generic.auto._ for automatic  conversions.

Lets walk through an example,

scala> import io.circe.syntax._ 
import io.circe.syntax._

scala> import io.circe.generic.auto._
import io.circe.generic.auto._

scala> case class MyClass(id: Int, name: String)
defined class MyClass

scala> val myClass = MyClass(1, "Class 1")
myClass: MyClass = MyClass(1,Class 1)

scala> myClass.asJson
res0: io.circe.Json =
{
 "id" : 1,
 "name" : "Class 1"
}

You can see, the conversion from json to the defined case class needs no implicits to be written explicity. Just an import works everything for simple class class conversions to Json or from Json to case class. Calling a method asJson on object gives me the corresponding json easily.

Similarly, the case is for decoding the case class object from Json,

scala> val decodedObject = jsonResult.as[MyClass]
decodedObject: io.circe.Decoder.Result[MyClass] = Right(MyClass(1,Class 1))

Again, the good thing is decoding functions returns Result[A] which is nothing but the type defined in the Decoder object as

type Result[A] = Either[DecodingFailure, A]

which is either letting you handle the failure while decoding the Json to the case class object.

scala> case class MyNewClass(id: Int, number: Int)
defined class MyNewClass

scala> val decodedObjectNew = jsonResult.as[MyNewClass]
decodedObjectNew: io.circe.Decoder.Result[MyNewClass] = Left(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(number))))

The above code returns the Left(DecodingFailure(“Failure message”)) where we try to map json to the wrong case class object.

Conclusion: This blog gives the brief introduction to the circe library which is a Json library for scala. It includes the basic examples to hands on with the circe library to handle the Json. This explains the way to parse, traverse  json  and, encode and decode case class for to and from Json.


KNOLDUS-advt-sticker

Written by 

Rachel Jones is a Solutions Lead at Knoldus Inc. having more than 22 years of experience. Rachel likes to delve deeper into the field of AI(Artificial Intelligence) and deep learning. She loves challenges and motivating people, also loves to read novels by Dan Brown. Rachel has problem solving, management and leadership skills moreover, she is familiar with programming languages such as Java, Scala, C++ & Html.