or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cursor-navigation.mderror-handling.mdindex.mdjson-data-types.mdjson-printing.mdkey-encoding-decoding.mdtype-classes.md
tile.json

tessl/maven-io-circe--circe-core

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.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.circe/circe-core_2.13@0.14.x

To install, run

npx @tessl/cli install tessl/maven-io-circe--circe-core@0.14.0

index.mddocs/

Circe Core

Circe is a comprehensive JSON library for Scala that enables developers to encode and decode JSON data with type safety and functional programming principles. The core module contains fundamental data types like Json, JsonObject, JsonNumber, and essential type classes including Encoder, Decoder, and Codec for converting between Scala values and JSON. It offers a cursor-based API for navigating and manipulating JSON structures, comprehensive error handling with detailed failure information, and seamless integration with the Cats functional programming ecosystem.

Package Information

  • Package Name: io.circe:circe-core_2.13
  • Package Type: maven
  • Language: Scala
  • Installation: libraryDependencies += "io.circe" %% "circe-core" % "0.14.13"

Core Imports

import io.circe._
import io.circe.syntax._

Import specific components:

import io.circe.{Json, JsonObject, JsonNumber, Encoder, Decoder, Codec}
import io.circe.{HCursor, ACursor, DecodingFailure, ParsingFailure}
import io.circe.{Printer, KeyEncoder, KeyDecoder}

Basic Usage

JSON Construction

import io.circe._
import io.circe.syntax._

// Direct JSON construction using factory methods
val json = Json.obj(
  "name" -> Json.fromString("John"),
  "age" -> Json.fromInt(30),
  "active" -> Json.fromBoolean(true),
  "scores" -> Json.arr(Json.fromInt(85), Json.fromInt(92), Json.fromInt(78))
)

// Using constants and collection builders
val nullValue = Json.Null
val trueValue = Json.True
val falseValue = Json.False
val arrayJson = Json.fromValues(List(Json.fromInt(1), Json.fromInt(2), Json.fromInt(3)))
val objectJson = Json.fromFields(List("key1" -> Json.fromString("value1"), "key2" -> Json.fromInt(42)))

Encoding to JSON

import io.circe._
import io.circe.syntax._

// Basic encoding with syntax extension
val name = "John"
val age = 30
val active = true

val nameJson = name.asJson         // Json
val ageJson = age.asJson           // Json
val activeJson = active.asJson     // Json

// Object encoding with key syntax operator
val personJson = Json.obj(
  "name" := "John",
  "age" := 30,
  "active" := true
)

// Type class based encoding
case class Person(name: String, age: Int, active: Boolean)
implicit val personEncoder: Encoder[Person] = Encoder.forProduct3("name", "age", "active")(p => (p.name, p.age, p.active))

val person = Person("John", 30, true)
val encoded = person.asJson     // Json

Decoding from JSON

import io.circe._
import io.circe.parser._

// Basic decoding
val json = parse("""{"name":"John","age":30,"active":true}""").getOrElse(Json.Null)

val cursor = json.hcursor
val name: Decoder.Result[String] = cursor.downField("name").as[String]
val age: Decoder.Result[Int] = cursor.downField("age").as[Int]

// Object decoding
implicit val personDecoder: Decoder[Person] = Decoder.forProduct3("name", "age", "active")(Person.apply)

val person: Either[DecodingFailure, Person] = json.as[Person]

Cursor Navigation

import io.circe._
import io.circe.parser._

val jsonString = """{"users":[{"name":"John","age":30},{"name":"Jane","age":25}]}"""
val json = parse(jsonString).getOrElse(Json.Null)

val cursor = json.hcursor
val firstUserName = cursor
  .downField("users")
  .downArray
  .downField("name")
  .as[String]  // Right("John")

JSON Manipulation

import io.circe._

val json1 = Json.obj("name" := "John", "age" := 30)
val json2 = Json.obj("age" := 31, "city" := "NYC")

// Deep merge JSON objects
val merged = json1.deepMerge(json2)  // {"name":"John","age":31,"city":"NYC"}

// Remove null values
val withNulls = Json.obj("name" := "John", "email" := Json.Null, "age" := 30)
val cleaned = withNulls.dropNullValues  // {"name":"John","age":30}

// Search for keys
val nested = Json.obj("user" := Json.obj("profile" := Json.obj("name" := "John")))
val names = nested.findAllByKey("name")  // List(Json.fromString("John"))
val namesAlt = nested \\ "name"          // Alternative syntax

JSON Printing

import io.circe._

val json = Json.obj("name" := "John", "age" := 30, "scores" := Json.arr(85, 92, 78))

// Built-in printing options
val compact = json.noSpaces           // {"name":"John","age":30,"scores":[85,92,78]}
val pretty = json.spaces2             // Pretty printed with 2-space indentation
val sorted = json.spaces2SortKeys     // Pretty printed with sorted keys

// Custom printer
val printer = Printer.noSpaces.copy(dropNullValues = true, sortKeys = true)
val custom = printer.print(json)

Architecture

Circe-core is built around several key abstractions:

  • Json: Immutable representation of JSON values with a rich API for manipulation
  • Type Classes: Encoder/Decoder pattern for type-safe conversion between Scala and JSON
  • Cursors: Zipper-like navigation API for traversing and modifying JSON structures
  • Error Handling: Comprehensive failure types with detailed path information
  • Printing: Configurable JSON formatting and output

Capabilities

JSON Data Types

Core data structures for representing JSON values with factory methods and manipulation APIs.

sealed abstract class Json {
  // Type checking
  def isNull: Boolean
  def isBoolean: Boolean
  def isNumber: Boolean
  def isString: Boolean
  def isArray: Boolean
  def isObject: Boolean
  
  // Pattern matching
  def fold[X](
    jsonNull: => X,
    jsonBoolean: Boolean => X,
    jsonNumber: JsonNumber => X,
    jsonString: String => X,
    jsonArray: Vector[Json] => X,
    jsonObject: JsonObject => X
  ): X
  
  // Manipulation
  def deepMerge(that: Json): Json
  def dropNullValues: Json
  def dropEmptyValues: Json
  def findAllByKey(key: String): List[Json]
  def \\(key: String): List[Json]
  
  // Printing
  def noSpaces: String
  def spaces2: String
  def spaces4: String
  def spaces2SortKeys: String
  def printWith(printer: Printer): String
}

object Json {
  // Constants
  val Null: Json
  val True: Json
  val False: Json
  
  // Factory methods
  def obj(fields: (String, Json)*): Json
  def arr(values: Json*): Json
  def fromFields(fields: Iterable[(String, Json)]): Json
  def fromValues(values: Iterable[Json]): Json
  def fromString(value: String): Json
  def fromBoolean(value: Boolean): Json
  def fromInt(value: Int): Json
  def fromLong(value: Long): Json
  def fromDouble(value: Double): Json
  def fromBigDecimal(value: BigDecimal): Json
}

final case class JsonObject {
  def apply(key: String): Option[Json]
  def contains(key: String): Boolean
  def size: Int
  def isEmpty: Boolean
  def keys: Iterable[String]
  def values: Iterable[Json]
  def add(key: String, value: Json): JsonObject
  def remove(key: String): JsonObject
  def mapValues(f: Json => Json): JsonObject
  def deepMerge(that: JsonObject): JsonObject
}

sealed abstract class JsonNumber {
  def toBigDecimal: Option[BigDecimal]
  def toBigInt: Option[BigInt]
  def toDouble: Double
  def toLong: Option[Long]
  def toInt: Option[Int]
}

JSON Data Types

Type Classes

Type-safe encoding and decoding between Scala types and JSON with combinators and instances.

trait Encoder[A] {
  def apply(a: A): Json
  def contramap[B](f: B => A): Encoder[B]
  def mapJson(f: Json => Json): Encoder[A]
}

object Encoder {
  def apply[A](implicit instance: Encoder[A]): Encoder[A]
  def instance[A](f: A => Json): Encoder[A]
  
  // Primitive instances
  implicit val encodeString: Encoder[String]
  implicit val encodeInt: Encoder[Int]
  implicit val encodeBoolean: Encoder[Boolean]
  implicit val encodeDouble: Encoder[Double]
  implicit val encodeBigDecimal: Encoder[BigDecimal]
  
  // Collection instances
  implicit def encodeList[A: Encoder]: Encoder[List[A]]
  implicit def encodeVector[A: Encoder]: Encoder[Vector[A]]
  implicit def encodeSet[A: Encoder]: Encoder[Set[A]]
  implicit def encodeMap[A: Encoder]: Encoder[Map[String, A]]
  implicit def encodeOption[A: Encoder]: Encoder[Option[A]]
  
  // Subtypes
  trait AsObject[A] extends Encoder[A] {
    def encodeObject(a: A): JsonObject
  }
  
  trait AsArray[A] extends Encoder[A] {
    def encodeArray(a: A): Vector[Json]
  }
}

trait Decoder[A] {
  def apply(c: HCursor): Decoder.Result[A]
  def map[B](f: A => B): Decoder[B]
  def flatMap[B](f: A => Decoder[B]): Decoder[B]
  def emap[B](f: A => Either[String, B]): Decoder[B]
  def ensure(pred: A => Boolean, message: => String): Decoder[A]
  def at(field: String): Decoder[A]
  def prepare(f: ACursor => ACursor): Decoder[A]
}

object Decoder {
  type Result[A] = Either[DecodingFailure, A]
  type AccumulatingResult[A] = ValidatedNel[DecodingFailure, A]
  
  def apply[A](implicit instance: Decoder[A]): Decoder[A]
  def instance[A](f: HCursor => Result[A]): Decoder[A]
  def const[A](a: A): Decoder[A]
  def failed[A](failure: DecodingFailure): Decoder[A]
  
  // Primitive instances
  implicit val decodeString: Decoder[String]
  implicit val decodeInt: Decoder[Int]
  implicit val decodeBoolean: Decoder[Boolean]
  implicit val decodeDouble: Decoder[Double]
  implicit val decodeBigDecimal: Decoder[BigDecimal]
  
  // Collection instances (similar to Encoder)
  implicit def decodeList[A: Decoder]: Decoder[List[A]]
  implicit def decodeVector[A: Decoder]: Decoder[Vector[A]]
  implicit def decodeOption[A: Decoder]: Decoder[Option[A]]
  implicit def decodeMap[A: Decoder]: Decoder[Map[String, A]]
}

trait Codec[A] extends Decoder[A] with Encoder[A] {
  def iemap[B](f: A => Either[String, B])(g: B => A): Codec[B]
}

object Codec {
  def from[A](decoder: Decoder[A], encoder: Encoder[A]): Codec[A]
}

Type Classes

Cursor Navigation

Zipper-like API for navigating and manipulating JSON structures with path tracking.

abstract class ACursor {
  def focus: Option[Json]
  def succeeded: Boolean
  def failed: Boolean
  def history: List[CursorOp]
  
  // Navigation
  def top: Option[Json]
  def up: ACursor
  def left: ACursor
  def right: ACursor
  def downField(k: String): ACursor
  def downArray: ACursor
  def downN(n: Int): ACursor
  def field(k: String): ACursor
  
  // Modification
  def withFocus(f: Json => Json): 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)(fallback: => A)(implicit d: Decoder[A]): A
}

abstract class HCursor extends ACursor {
  def value: Json
  def root: HCursor
  def keys: Option[Iterable[String]]
  def values: Option[Iterable[Json]]
}

object HCursor {
  def fromJson(value: Json): HCursor
}

sealed abstract class CursorOp {
  def requiresArray: Boolean
  def requiresObject: Boolean
}

object CursorOp {
  case object MoveLeft extends CursorOp
  case object MoveRight extends CursorOp
  case object MoveUp extends CursorOp
  case object DownArray extends CursorOp
  case class DownField(k: String) extends CursorOp
  case class DownN(n: Int) extends CursorOp
  case class Field(k: String) extends CursorOp
}

Cursor Navigation

Error Handling

Comprehensive error types with detailed failure information and path tracking.

sealed abstract class Error extends Exception

final case class ParsingFailure(
  message: String, 
  underlying: Throwable
) extends Error

sealed abstract class DecodingFailure extends Error {
  def message: String
  def history: List[CursorOp]
  def pathToRootString: Option[String]
  def reason: DecodingFailure.Reason
  
  def withMessage(message: String): DecodingFailure
  def withReason(reason: DecodingFailure.Reason): DecodingFailure
}

object DecodingFailure {
  sealed abstract class Reason
  
  object Reason {
    case object MissingField extends Reason
    case class WrongTypeExpectation(expected: String, got: Json) extends Reason
    case class CustomReason(message: String) extends Reason
  }
  
  def apply(message: String, ops: List[CursorOp]): DecodingFailure
  def apply(message: String, cursor: ACursor): DecodingFailure
}

final case class Errors(errors: NonEmptyList[Error]) extends Exception

Error Handling

Key Encoding and Decoding

Type-safe conversion of object keys between Scala types and strings.

trait KeyEncoder[A] {
  def apply(key: A): String
}

trait KeyDecoder[A] {
  def apply(key: String): Option[A]
}

Key Encoding and Decoding

JSON Printing

Configurable JSON formatting and output with extensive customization options.

final case class Printer(
  dropNullValues: Boolean,
  indent: String,
  lbraceLeft: String,
  lbraceRight: String,
  rbraceLeft: String,
  rbraceRight: String,
  lbracketLeft: String,
  lbracketRight: String,
  rbracketLeft: String,
  rbracketRight: String,
  lrbracketsEmpty: String,
  arrayCommaLeft: String,
  arrayCommaRight: String,
  objectCommaLeft: String,
  objectCommaRight: String,
  colonLeft: String,
  colonRight: String,
  escapeNonAscii: Boolean,
  sortKeys: Boolean,
  reuseWriters: Boolean,
  predictSize: Boolean
) {
  def print(json: Json): String
  def printToByteBuffer(json: Json): ByteBuffer
  def withSortedKeys: Printer
}

object Printer {
  val noSpaces: Printer
  val spaces2: Printer
  val spaces4: Printer
  val noSpacesSortKeys: Printer
  val spaces2SortKeys: Printer
  val spaces4SortKeys: Printer
  
  def indented(indent: String, sortKeys: Boolean = false): Printer
}

JSON Printing

Types

Core Result Types

object Decoder {
  type Result[A] = Either[DecodingFailure, A]
  type AccumulatingResult[A] = ValidatedNel[DecodingFailure, A]
}

sealed abstract class CursorOp

Extension Types

// Syntax extensions for encoding
implicit class EncoderOps[A](private val value: A) extends AnyVal {
  def asJson(implicit encoder: Encoder[A]): Json
  def asJsonObject(implicit encoder: Encoder.AsObject[A]): JsonObject
}

// Syntax extensions for object key creation
implicit class KeyOps[K](private val value: K) extends AnyVal {
  def :=[A: Encoder](a: A)(implicit keyEncoder: KeyEncoder[K]): (String, Json)
}

// Syntax extensions for JSON values
implicit class JsonOps(private val json: Json) extends AnyVal {
  def hcursor: HCursor
  def as[A](implicit decoder: Decoder[A]): Decoder.Result[A]
  def asAccumulating[A](implicit decoder: Decoder[A]): Decoder.AccumulatingResult[A]
  def \\(key: String): List[Json]
  def findAllByKey(key: String): List[Json]
}

// Syntax extensions for cursors
implicit class ACursorOps(private val cursor: ACursor) extends AnyVal {
  def as[A](implicit decoder: Decoder[A]): Decoder.Result[A]
  def get[A](key: String)(implicit decoder: Decoder[A]): Decoder.Result[A]
  def getOrElse[A](key: String)(fallback: => A)(implicit decoder: Decoder[A]): A
}