Core module of circe, a JSON library for Scala that enables developers to encode and decode JSON data with type safety and functional programming principles.
—
Circe provides a zipper-like cursor API for navigating and manipulating JSON structures. Cursors allow safe traversal of JSON documents with comprehensive error tracking.
Abstract base class for all cursor types.
abstract class ACursor {
// Current state
def focus: Option[Json]
def succeeded: Boolean
def failed: Boolean
def success: Option[HCursor]
// History and path
def history: List[CursorOp]
def pathString: String
def pathToRoot: PathToRoot
// Navigation
def up: ACursor
def left: ACursor
def right: ACursor
def downArray: ACursor
def downN(n: Int): ACursor
def downField(k: String): ACursor
def downFields(k1: String, k2: String, ks: String*): ACursor
def field(k: String): ACursor
def find(p: Json => Boolean): ACursor
def leftN(n: Int): ACursor
def rightN(n: Int): ACursor
def leftAt(p: Json => Boolean): ACursor
def rightAt(p: Json => Boolean): ACursor
// Array access
def values: Option[Iterable[Json]]
def index: Option[Int]
// Object access
def keys: Option[Iterable[String]]
def key: Option[String]
def field(k: String): Option[Json]
// Transformation
def withFocus(f: Json => Json): ACursor
def withFocusM[F[_]](f: Json => F[Json])(implicit F: Applicative[F]): F[ACursor]
def set(j: Json): ACursor
def delete: ACursor
// Decoding
def as[A](implicit d: Decoder[A]): Decoder.Result[A]
def get[A](k: String)(implicit d: Decoder[A]): Decoder.Result[A]
def getOrElse[A](k: String)(default: => A)(implicit d: Decoder[A]): A
// Replay operations
def replayOne(op: CursorOp): ACursor
def replay(history: List[CursorOp]): ACursor
// Root navigation
def top: Option[Json]
def root: HCursor
}Successful cursor with a guaranteed focus.
abstract class HCursor extends ACursor {
// Guaranteed focus
def value: Json
// Always successful
override def succeeded: Boolean = true
override def focus: Option[Json] = Some(value)
override def success: Option[HCursor] = Some(this)
// Abstract methods for implementations
def replace(newValue: Json, cursor: HCursor, op: CursorOp): HCursor
def addOp(cursor: HCursor, op: CursorOp): HCursor
}object HCursor {
def fromJson(value: Json): HCursor
}Cursor representing failed navigation.
final class FailedCursor extends ACursor {
// Failure information
def incorrectFocus: Boolean
def missingField: Boolean
// Always failed
override def succeeded: Boolean = false
override def failed: Boolean = true
override def focus: Option[Json] = None
override def success: Option[HCursor] = None
// All navigation operations return this (no-ops)
override def up: ACursor = this
override def left: ACursor = this
override def right: ACursor = this
// ... etc for all navigation methods
}Represents cursor navigation operations for error tracking.
sealed abstract class CursorOp
// Basic navigation
case object MoveLeft extends CursorOp
case object MoveRight extends CursorOp
case object MoveUp extends CursorOp
// Field operations
final case class Field(k: String) extends CursorOp
final case class DownField(k: String) extends CursorOp
// Array operations
case object DownArray extends CursorOp
final case class DownN(n: Int) extends CursorOp
// Delete operation
case object DeleteGoParent extends CursorOpobject CursorOp {
// Convert operations to path string
def opsToPath(history: List[CursorOp]): String
// Operation categories
abstract class ObjectOp extends CursorOp
abstract class ArrayOp extends CursorOp
abstract class UnconstrainedOp extends CursorOp
}Represents the path from cursor position to document root.
final case class PathToRoot(elems: List[PathElem]) {
def asPathString: String
}
sealed abstract class PathElem
final case class ObjectKey(key: String) extends PathElem
final case class ArrayIndex(index: Long) extends PathElemobject PathToRoot {
val empty: PathToRoot
def fromHistory(history: List[CursorOp]): Either[String, PathToRoot]
def toPathString(pathToRoot: PathToRoot): String
}import io.circe._
import io.circe.parser._
val jsonString = """
{
"users": [
{"name": "John", "age": 30, "address": {"city": "NYC"}},
{"name": "Jane", "age": 25, "address": {"city": "LA"}}
],
"total": 2
}
"""
val json = parse(jsonString).getOrElse(Json.Null)
val cursor = json.hcursor
// Navigate to first user's name
val firstName = cursor
.downField("users")
.downArray
.downField("name")
.as[String]
// Result: Right("John")
// Navigate to second user's city
val secondCity = cursor
.downField("users")
.downN(1)
.downField("address")
.downField("city")
.as[String]
// Result: Right("LA")
// Get total count
val total = cursor.downField("total").as[Int]
// Result: Right(2)import io.circe._
import io.circe.parser._
val json = parse("""{"name": "John", "age": 30}""").getOrElse(Json.Null)
val cursor = json.hcursor
// Successful navigation
val name = cursor.downField("name").as[String]
// Result: Right("John")
// Failed navigation - missing field
val emailCursor = cursor.downField("email")
println(emailCursor.succeeded) // false
println(emailCursor.failed) // true
val email = emailCursor.as[String]
// Result: Left(DecodingFailure(Missing required field, List(DownField(email))))
// Failed navigation - wrong type
val ageCursor = cursor.downField("age").downArray
println(ageCursor.succeeded) // false
// Check failure reasons for FailedCursor
emailCursor match {
case failed: FailedCursor =>
println(failed.missingField) // true
println(failed.incorrectFocus) // false
case _ => // ...
}import io.circe._
import io.circe.parser._
val json = parse("""{"name": "John", "age": 30}""").getOrElse(Json.Null)
val cursor = json.hcursor
// Modify a field
val modifiedCursor = cursor
.downField("name")
.withFocus(_.asString.map(name => Json.fromString(name.toUpperCase)).getOrElse(Json.Null))
val newJson = modifiedCursor.top.getOrElse(Json.Null)
// Result: {"name": "JOHN", "age": 30}
// Set a field value
val updatedCursor = cursor
.downField("age")
.set(Json.fromInt(31))
val updatedJson = updatedCursor.top.getOrElse(Json.Null)
// Result: {"name": "John", "age": 31}
// Delete a field
val deletedCursor = cursor
.downField("age")
.delete
val deletedJson = deletedCursor.top.getOrElse(Json.Null)
// Result: {"name": "John"}import io.circe._
import io.circe.parser._
val json = parse("""{"items": [1, 2, 3, 4, 5]}""").getOrElse(Json.Null)
val cursor = json.hcursor
// Navigate to array
val arraysCursor = cursor.downField("items").downArray
// Get first element
val first = arraysCursor.as[Int]
// Result: Right(1)
// Navigate to specific indices
val third = cursor.downField("items").downN(2).as[Int]
// Result: Right(3)
// Navigate between array elements
val second = arraysCursor.right.as[Int]
// Result: Right(2)
val fourth = arraysCursor.right.right.right.as[Int]
// Result: Right(4)
// Access array information
val itemsCursor = cursor.downField("items")
val values = itemsCursor.values
// Result: Some(Vector(1, 2, 3, 4, 5))import io.circe._
import io.circe.parser._
val json = parse("""
{
"company": {
"departments": [
{
"name": "Engineering",
"employees": [
{"name": "Alice", "role": "Engineer"},
{"name": "Bob", "role": "Senior Engineer"}
]
},
{
"name": "Sales",
"employees": [
{"name": "Charlie", "role": "Sales Rep"}
]
}
]
}
}
""").getOrElse(Json.Null)
val cursor = json.hcursor
// Navigate to first department name
val firstDeptName = cursor
.downField("company")
.downField("departments")
.downArray
.downField("name")
.as[String]
// Result: Right("Engineering")
// Navigate to Alice's role
val aliceRole = cursor
.downField("company")
.downField("departments")
.downN(0)
.downField("employees")
.downN(0)
.downField("role")
.as[String]
// Result: Right("Engineer")
// Use downFields for multiple field navigation
val charlieRole = cursor
.downFields("company", "departments")
.downN(1)
.downFields("employees")
.downArray
.downField("role")
.as[String]
// Result: Right("Sales Rep")import io.circe._
import io.circe.parser._
val json = parse("""
{
"users": [
{"name": "John", "active": true},
{"name": "Jane", "active": false},
{"name": "Bob", "active": true}
]
}
""").getOrElse(Json.Null)
val cursor = json.hcursor
// Find first active user
val activeUserCursor = cursor
.downField("users")
.downArray
.find(_.hcursor.downField("active").as[Boolean].contains(true))
val activeUserName = activeUserCursor.downField("name").as[String]
// Result: Right("John")
// Find by name
val janeCursor = cursor
.downField("users")
.downArray
.find(_.hcursor.downField("name").as[String].contains("Jane"))
val janeActive = janeCursor.downField("active").as[Boolean]
// Result: Right(false)import io.circe._
import io.circe.parser._
val json = parse("""{"user": {"profile": {"name": "John"}}}""").getOrElse(Json.Null)
val cursor = json.hcursor
val nameCursor = cursor
.downField("user")
.downField("profile")
.downField("name")
// Get path information
println(nameCursor.pathString)
// Result: ".user.profile.name"
// Get operation history
println(nameCursor.history)
// Result: List(DownField(user), DownField(profile), DownField(name))
// Failed navigation path tracking
val failedCursor = cursor.downField("nonexistent").downField("field")
println(failedCursor.pathString)
// Result: ".nonexistent.field"import io.circe._
val cursor: HCursor = Json.obj("name" -> Json.fromString("John")).hcursor
// Helper methods for safe access
def getName(c: HCursor): String =
c.get[String]("name").getOrElse("Unknown")
def getAge(c: HCursor): Int =
c.getOrElse[Int]("age")(0)
// Replay operations on different JSON
val operations = List(CursorOp.DownField("user"), CursorOp.DownField("name"))
val newJson = Json.obj("user" -> Json.obj("name" -> Json.fromString("Jane")))
val replayedCursor = newJson.hcursor.replay(operations)
// Result: cursor positioned at "Jane"Install with Tessl CLI
npx tessl i tessl/maven-io-circe--circe-core