0
# Circe Core
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: io.circe:circe-core_2.13
7
- **Package Type**: maven
8
- **Language**: Scala
9
- **Installation**: `libraryDependencies += "io.circe" %% "circe-core" % "0.14.13"`
10
11
## Core Imports
12
13
```scala
14
import io.circe._
15
import io.circe.syntax._
16
```
17
18
Import specific components:
19
20
```scala
21
import io.circe.{Json, JsonObject, JsonNumber, Encoder, Decoder, Codec}
22
import io.circe.{HCursor, ACursor, DecodingFailure, ParsingFailure}
23
import io.circe.{Printer, KeyEncoder, KeyDecoder}
24
```
25
26
## Basic Usage
27
28
### JSON Construction
29
30
```scala
31
import io.circe._
32
import io.circe.syntax._
33
34
// Direct JSON construction using factory methods
35
val json = Json.obj(
36
"name" -> Json.fromString("John"),
37
"age" -> Json.fromInt(30),
38
"active" -> Json.fromBoolean(true),
39
"scores" -> Json.arr(Json.fromInt(85), Json.fromInt(92), Json.fromInt(78))
40
)
41
42
// Using constants and collection builders
43
val nullValue = Json.Null
44
val trueValue = Json.True
45
val falseValue = Json.False
46
val arrayJson = Json.fromValues(List(Json.fromInt(1), Json.fromInt(2), Json.fromInt(3)))
47
val objectJson = Json.fromFields(List("key1" -> Json.fromString("value1"), "key2" -> Json.fromInt(42)))
48
```
49
50
### Encoding to JSON
51
52
```scala
53
import io.circe._
54
import io.circe.syntax._
55
56
// Basic encoding with syntax extension
57
val name = "John"
58
val age = 30
59
val active = true
60
61
val nameJson = name.asJson // Json
62
val ageJson = age.asJson // Json
63
val activeJson = active.asJson // Json
64
65
// Object encoding with key syntax operator
66
val personJson = Json.obj(
67
"name" := "John",
68
"age" := 30,
69
"active" := true
70
)
71
72
// Type class based encoding
73
case class Person(name: String, age: Int, active: Boolean)
74
implicit val personEncoder: Encoder[Person] = Encoder.forProduct3("name", "age", "active")(p => (p.name, p.age, p.active))
75
76
val person = Person("John", 30, true)
77
val encoded = person.asJson // Json
78
```
79
80
### Decoding from JSON
81
82
```scala
83
import io.circe._
84
import io.circe.parser._
85
86
// Basic decoding
87
val json = parse("""{"name":"John","age":30,"active":true}""").getOrElse(Json.Null)
88
89
val cursor = json.hcursor
90
val name: Decoder.Result[String] = cursor.downField("name").as[String]
91
val age: Decoder.Result[Int] = cursor.downField("age").as[Int]
92
93
// Object decoding
94
implicit val personDecoder: Decoder[Person] = Decoder.forProduct3("name", "age", "active")(Person.apply)
95
96
val person: Either[DecodingFailure, Person] = json.as[Person]
97
```
98
99
### Cursor Navigation
100
101
```scala
102
import io.circe._
103
import io.circe.parser._
104
105
val jsonString = """{"users":[{"name":"John","age":30},{"name":"Jane","age":25}]}"""
106
val json = parse(jsonString).getOrElse(Json.Null)
107
108
val cursor = json.hcursor
109
val firstUserName = cursor
110
.downField("users")
111
.downArray
112
.downField("name")
113
.as[String] // Right("John")
114
```
115
116
### JSON Manipulation
117
118
```scala
119
import io.circe._
120
121
val json1 = Json.obj("name" := "John", "age" := 30)
122
val json2 = Json.obj("age" := 31, "city" := "NYC")
123
124
// Deep merge JSON objects
125
val merged = json1.deepMerge(json2) // {"name":"John","age":31,"city":"NYC"}
126
127
// Remove null values
128
val withNulls = Json.obj("name" := "John", "email" := Json.Null, "age" := 30)
129
val cleaned = withNulls.dropNullValues // {"name":"John","age":30}
130
131
// Search for keys
132
val nested = Json.obj("user" := Json.obj("profile" := Json.obj("name" := "John")))
133
val names = nested.findAllByKey("name") // List(Json.fromString("John"))
134
val namesAlt = nested \\ "name" // Alternative syntax
135
```
136
137
### JSON Printing
138
139
```scala
140
import io.circe._
141
142
val json = Json.obj("name" := "John", "age" := 30, "scores" := Json.arr(85, 92, 78))
143
144
// Built-in printing options
145
val compact = json.noSpaces // {"name":"John","age":30,"scores":[85,92,78]}
146
val pretty = json.spaces2 // Pretty printed with 2-space indentation
147
val sorted = json.spaces2SortKeys // Pretty printed with sorted keys
148
149
// Custom printer
150
val printer = Printer.noSpaces.copy(dropNullValues = true, sortKeys = true)
151
val custom = printer.print(json)
152
```
153
154
## Architecture
155
156
Circe-core is built around several key abstractions:
157
158
- **Json**: Immutable representation of JSON values with a rich API for manipulation
159
- **Type Classes**: Encoder/Decoder pattern for type-safe conversion between Scala and JSON
160
- **Cursors**: Zipper-like navigation API for traversing and modifying JSON structures
161
- **Error Handling**: Comprehensive failure types with detailed path information
162
- **Printing**: Configurable JSON formatting and output
163
164
## Capabilities
165
166
### JSON Data Types
167
168
Core data structures for representing JSON values with factory methods and manipulation APIs.
169
170
```scala { .api }
171
sealed abstract class Json {
172
// Type checking
173
def isNull: Boolean
174
def isBoolean: Boolean
175
def isNumber: Boolean
176
def isString: Boolean
177
def isArray: Boolean
178
def isObject: Boolean
179
180
// Pattern matching
181
def fold[X](
182
jsonNull: => X,
183
jsonBoolean: Boolean => X,
184
jsonNumber: JsonNumber => X,
185
jsonString: String => X,
186
jsonArray: Vector[Json] => X,
187
jsonObject: JsonObject => X
188
): X
189
190
// Manipulation
191
def deepMerge(that: Json): Json
192
def dropNullValues: Json
193
def dropEmptyValues: Json
194
def findAllByKey(key: String): List[Json]
195
def \\(key: String): List[Json]
196
197
// Printing
198
def noSpaces: String
199
def spaces2: String
200
def spaces4: String
201
def spaces2SortKeys: String
202
def printWith(printer: Printer): String
203
}
204
205
object Json {
206
// Constants
207
val Null: Json
208
val True: Json
209
val False: Json
210
211
// Factory methods
212
def obj(fields: (String, Json)*): Json
213
def arr(values: Json*): Json
214
def fromFields(fields: Iterable[(String, Json)]): Json
215
def fromValues(values: Iterable[Json]): Json
216
def fromString(value: String): Json
217
def fromBoolean(value: Boolean): Json
218
def fromInt(value: Int): Json
219
def fromLong(value: Long): Json
220
def fromDouble(value: Double): Json
221
def fromBigDecimal(value: BigDecimal): Json
222
}
223
224
final case class JsonObject {
225
def apply(key: String): Option[Json]
226
def contains(key: String): Boolean
227
def size: Int
228
def isEmpty: Boolean
229
def keys: Iterable[String]
230
def values: Iterable[Json]
231
def add(key: String, value: Json): JsonObject
232
def remove(key: String): JsonObject
233
def mapValues(f: Json => Json): JsonObject
234
def deepMerge(that: JsonObject): JsonObject
235
}
236
237
sealed abstract class JsonNumber {
238
def toBigDecimal: Option[BigDecimal]
239
def toBigInt: Option[BigInt]
240
def toDouble: Double
241
def toLong: Option[Long]
242
def toInt: Option[Int]
243
}
244
```
245
246
[JSON Data Types](./json-data-types.md)
247
248
### Type Classes
249
250
Type-safe encoding and decoding between Scala types and JSON with combinators and instances.
251
252
```scala { .api }
253
trait Encoder[A] {
254
def apply(a: A): Json
255
def contramap[B](f: B => A): Encoder[B]
256
def mapJson(f: Json => Json): Encoder[A]
257
}
258
259
object Encoder {
260
def apply[A](implicit instance: Encoder[A]): Encoder[A]
261
def instance[A](f: A => Json): Encoder[A]
262
263
// Primitive instances
264
implicit val encodeString: Encoder[String]
265
implicit val encodeInt: Encoder[Int]
266
implicit val encodeBoolean: Encoder[Boolean]
267
implicit val encodeDouble: Encoder[Double]
268
implicit val encodeBigDecimal: Encoder[BigDecimal]
269
270
// Collection instances
271
implicit def encodeList[A: Encoder]: Encoder[List[A]]
272
implicit def encodeVector[A: Encoder]: Encoder[Vector[A]]
273
implicit def encodeSet[A: Encoder]: Encoder[Set[A]]
274
implicit def encodeMap[A: Encoder]: Encoder[Map[String, A]]
275
implicit def encodeOption[A: Encoder]: Encoder[Option[A]]
276
277
// Subtypes
278
trait AsObject[A] extends Encoder[A] {
279
def encodeObject(a: A): JsonObject
280
}
281
282
trait AsArray[A] extends Encoder[A] {
283
def encodeArray(a: A): Vector[Json]
284
}
285
}
286
287
trait Decoder[A] {
288
def apply(c: HCursor): Decoder.Result[A]
289
def map[B](f: A => B): Decoder[B]
290
def flatMap[B](f: A => Decoder[B]): Decoder[B]
291
def emap[B](f: A => Either[String, B]): Decoder[B]
292
def ensure(pred: A => Boolean, message: => String): Decoder[A]
293
def at(field: String): Decoder[A]
294
def prepare(f: ACursor => ACursor): Decoder[A]
295
}
296
297
object Decoder {
298
type Result[A] = Either[DecodingFailure, A]
299
type AccumulatingResult[A] = ValidatedNel[DecodingFailure, A]
300
301
def apply[A](implicit instance: Decoder[A]): Decoder[A]
302
def instance[A](f: HCursor => Result[A]): Decoder[A]
303
def const[A](a: A): Decoder[A]
304
def failed[A](failure: DecodingFailure): Decoder[A]
305
306
// Primitive instances
307
implicit val decodeString: Decoder[String]
308
implicit val decodeInt: Decoder[Int]
309
implicit val decodeBoolean: Decoder[Boolean]
310
implicit val decodeDouble: Decoder[Double]
311
implicit val decodeBigDecimal: Decoder[BigDecimal]
312
313
// Collection instances (similar to Encoder)
314
implicit def decodeList[A: Decoder]: Decoder[List[A]]
315
implicit def decodeVector[A: Decoder]: Decoder[Vector[A]]
316
implicit def decodeOption[A: Decoder]: Decoder[Option[A]]
317
implicit def decodeMap[A: Decoder]: Decoder[Map[String, A]]
318
}
319
320
trait Codec[A] extends Decoder[A] with Encoder[A] {
321
def iemap[B](f: A => Either[String, B])(g: B => A): Codec[B]
322
}
323
324
object Codec {
325
def from[A](decoder: Decoder[A], encoder: Encoder[A]): Codec[A]
326
}
327
```
328
329
[Type Classes](./type-classes.md)
330
331
### Cursor Navigation
332
333
Zipper-like API for navigating and manipulating JSON structures with path tracking.
334
335
```scala { .api }
336
abstract class ACursor {
337
def focus: Option[Json]
338
def succeeded: Boolean
339
def failed: Boolean
340
def history: List[CursorOp]
341
342
// Navigation
343
def top: Option[Json]
344
def up: ACursor
345
def left: ACursor
346
def right: ACursor
347
def downField(k: String): ACursor
348
def downArray: ACursor
349
def downN(n: Int): ACursor
350
def field(k: String): ACursor
351
352
// Modification
353
def withFocus(f: Json => Json): ACursor
354
def set(j: Json): ACursor
355
def delete: ACursor
356
357
// Decoding
358
def as[A](implicit d: Decoder[A]): Decoder.Result[A]
359
def get[A](k: String)(implicit d: Decoder[A]): Decoder.Result[A]
360
def getOrElse[A](k: String)(fallback: => A)(implicit d: Decoder[A]): A
361
}
362
363
abstract class HCursor extends ACursor {
364
def value: Json
365
def root: HCursor
366
def keys: Option[Iterable[String]]
367
def values: Option[Iterable[Json]]
368
}
369
370
object HCursor {
371
def fromJson(value: Json): HCursor
372
}
373
374
sealed abstract class CursorOp {
375
def requiresArray: Boolean
376
def requiresObject: Boolean
377
}
378
379
object CursorOp {
380
case object MoveLeft extends CursorOp
381
case object MoveRight extends CursorOp
382
case object MoveUp extends CursorOp
383
case object DownArray extends CursorOp
384
case class DownField(k: String) extends CursorOp
385
case class DownN(n: Int) extends CursorOp
386
case class Field(k: String) extends CursorOp
387
}
388
```
389
390
[Cursor Navigation](./cursor-navigation.md)
391
392
### Error Handling
393
394
Comprehensive error types with detailed failure information and path tracking.
395
396
```scala { .api }
397
sealed abstract class Error extends Exception
398
399
final case class ParsingFailure(
400
message: String,
401
underlying: Throwable
402
) extends Error
403
404
sealed abstract class DecodingFailure extends Error {
405
def message: String
406
def history: List[CursorOp]
407
def pathToRootString: Option[String]
408
def reason: DecodingFailure.Reason
409
410
def withMessage(message: String): DecodingFailure
411
def withReason(reason: DecodingFailure.Reason): DecodingFailure
412
}
413
414
object DecodingFailure {
415
sealed abstract class Reason
416
417
object Reason {
418
case object MissingField extends Reason
419
case class WrongTypeExpectation(expected: String, got: Json) extends Reason
420
case class CustomReason(message: String) extends Reason
421
}
422
423
def apply(message: String, ops: List[CursorOp]): DecodingFailure
424
def apply(message: String, cursor: ACursor): DecodingFailure
425
}
426
427
final case class Errors(errors: NonEmptyList[Error]) extends Exception
428
```
429
430
[Error Handling](./error-handling.md)
431
432
### Key Encoding and Decoding
433
434
Type-safe conversion of object keys between Scala types and strings.
435
436
```scala { .api }
437
trait KeyEncoder[A] {
438
def apply(key: A): String
439
}
440
441
trait KeyDecoder[A] {
442
def apply(key: String): Option[A]
443
}
444
```
445
446
[Key Encoding and Decoding](./key-encoding-decoding.md)
447
448
### JSON Printing
449
450
Configurable JSON formatting and output with extensive customization options.
451
452
```scala { .api }
453
final case class Printer(
454
dropNullValues: Boolean,
455
indent: String,
456
lbraceLeft: String,
457
lbraceRight: String,
458
rbraceLeft: String,
459
rbraceRight: String,
460
lbracketLeft: String,
461
lbracketRight: String,
462
rbracketLeft: String,
463
rbracketRight: String,
464
lrbracketsEmpty: String,
465
arrayCommaLeft: String,
466
arrayCommaRight: String,
467
objectCommaLeft: String,
468
objectCommaRight: String,
469
colonLeft: String,
470
colonRight: String,
471
escapeNonAscii: Boolean,
472
sortKeys: Boolean,
473
reuseWriters: Boolean,
474
predictSize: Boolean
475
) {
476
def print(json: Json): String
477
def printToByteBuffer(json: Json): ByteBuffer
478
def withSortedKeys: Printer
479
}
480
481
object Printer {
482
val noSpaces: Printer
483
val spaces2: Printer
484
val spaces4: Printer
485
val noSpacesSortKeys: Printer
486
val spaces2SortKeys: Printer
487
val spaces4SortKeys: Printer
488
489
def indented(indent: String, sortKeys: Boolean = false): Printer
490
}
491
```
492
493
[JSON Printing](./json-printing.md)
494
495
## Types
496
497
### Core Result Types
498
499
```scala { .api }
500
object Decoder {
501
type Result[A] = Either[DecodingFailure, A]
502
type AccumulatingResult[A] = ValidatedNel[DecodingFailure, A]
503
}
504
505
sealed abstract class CursorOp
506
```
507
508
### Extension Types
509
510
```scala { .api }
511
// Syntax extensions for encoding
512
implicit class EncoderOps[A](private val value: A) extends AnyVal {
513
def asJson(implicit encoder: Encoder[A]): Json
514
def asJsonObject(implicit encoder: Encoder.AsObject[A]): JsonObject
515
}
516
517
// Syntax extensions for object key creation
518
implicit class KeyOps[K](private val value: K) extends AnyVal {
519
def :=[A: Encoder](a: A)(implicit keyEncoder: KeyEncoder[K]): (String, Json)
520
}
521
522
// Syntax extensions for JSON values
523
implicit class JsonOps(private val json: Json) extends AnyVal {
524
def hcursor: HCursor
525
def as[A](implicit decoder: Decoder[A]): Decoder.Result[A]
526
def asAccumulating[A](implicit decoder: Decoder[A]): Decoder.AccumulatingResult[A]
527
def \\(key: String): List[Json]
528
def findAllByKey(key: String): List[Json]
529
}
530
531
// Syntax extensions for cursors
532
implicit class ACursorOps(private val cursor: ACursor) extends AnyVal {
533
def as[A](implicit decoder: Decoder[A]): Decoder.Result[A]
534
def get[A](key: String)(implicit decoder: Decoder[A]): Decoder.Result[A]
535
def getOrElse[A](key: String)(fallback: => A)(implicit decoder: Decoder[A]): A
536
}
537
```