Traversing and modifying JSON

Working with JSON in circe usually involves using a cursor. Cursors are used both for extracting data and for performing modification.

Suppose we have the following JSON document:

import cats.syntax.either._
import io.circe._, io.circe.parser._

val json: String = """
  {
    "id": "c730433b-082c-4984-9d66-855c243266f0",
    "name": "Foo",
    "counts": [1, 2, 3],
    "values": {
      "bar": true,
      "baz": 100.001,
      "qux": ["a", "b"]
    }
  }
"""

val doc: Json = parse(json).getOrElse(Json.Null)

Extracting data

In order to traverse the document we need to create an HCursor with the focus at the document's root:

val cursor: HCursor = doc.hcursor

We can then use various operations to move the focus of the cursor around the document and extract data from it:

val baz: Decoder.Result[Double] =
  cursor.downField("values").downField("baz").as[Double]
// baz: Decoder.Result[Double] = Right(value = 100.001)

// You can also use `get[A](key)` as shorthand for `downField(key).as[A]`
val baz2: Decoder.Result[Double] =
  cursor.downField("values").get[Double]("baz")
// baz2: Decoder.Result[Double] = Right(value = 100.001)

val secondQux: Decoder.Result[String] =
  cursor.downField("values").downField("qux").downArray.as[String]
// secondQux: Decoder.Result[String] = Right(value = "a")

Transforming data

We can also use a cursor to modify JSON.

val reversedNameCursor: ACursor =
  cursor.downField("name").withFocus(_.mapString(_.reverse))

We can then return to the root of the document and return its value with top:

val reversedName: Option[Json] = reversedNameCursor.top
// reversedName: Option[Json] = Some(
//   value = JObject(
//     value = object[id -> "c730433b-082c-4984-9d66-855c243266f0",name -> "ooF",counts -> [
//   1,
//   2,
//   3
// ],values -> {
//   "bar" : true,
//   "baz" : 100.001,
//   "qux" : [
//     "a",
//     "b"
//   ]
// }]
//   )
// )

The result contains the original document with the "name" field reversed.

Note that Json is immutable, so the original document is left unchanged.

Cursors

circe has three slightly different cursor implementations:

Optics

Optics are an alternative way to traverse JSON documents. See the Optics page for more details.