0
# JSON Printing
1
2
Circe provides flexible and configurable JSON printing through the Printer class. It offers various formatting options, performance optimizations, and output formats.
3
4
## Printer
5
6
Configurable JSON pretty-printer for formatting JSON output.
7
8
```scala { .api }
9
final case class Printer(
10
dropNullValues: Boolean,
11
indent: String,
12
lbraceLeft: String = "",
13
lbraceRight: String = "",
14
rbraceLeft: String = "",
15
rbraceRight: String = "",
16
lbracketLeft: String = "",
17
lbracketRight: String = "",
18
rbracketLeft: String = "",
19
rbracketRight: String = "",
20
lrbracketsEmpty: String = "",
21
arrayCommaLeft: String = "",
22
arrayCommaRight: String = "",
23
objectCommaLeft: String = "",
24
objectCommaRight: String = "",
25
colonLeft: String = "",
26
colonRight: String = "",
27
reuseWriters: Boolean = false,
28
predictSize: Boolean = false,
29
escapeNonAscii: Boolean = false,
30
sortKeys: Boolean = false
31
) {
32
// Core printing methods
33
def print(json: Json): String
34
def printToByteBuffer(json: Json, cs: Charset): ByteBuffer
35
def printToByteBuffer(json: Json): ByteBuffer
36
def unsafePrintToAppendable(json: Json, out: Appendable): Unit
37
38
// Configuration
39
def withSortedKeys: Printer
40
}
41
```
42
43
### Printer Companion Object
44
45
```scala { .api }
46
object Printer {
47
// Predefined configurations
48
val noSpaces: Printer
49
val noSpacesSortKeys: Printer
50
val spaces2: Printer
51
val spaces2SortKeys: Printer
52
val spaces4: Printer
53
val spaces4SortKeys: Printer
54
55
// Custom indentation
56
def indented(indent: String, sortKeys: Boolean = false): Printer
57
}
58
```
59
60
## Usage Examples
61
62
### Basic Printing
63
64
```scala
65
import io.circe._
66
import io.circe.syntax._
67
68
val json = Json.obj(
69
"name" -> Json.fromString("John"),
70
"age" -> Json.fromInt(30),
71
"active" -> Json.fromBoolean(true),
72
"scores" -> Json.arr(Json.fromInt(85), Json.fromInt(92), Json.fromInt(78))
73
)
74
75
// Compact printing (no spaces)
76
val compact = json.noSpaces
77
// Result: {"name":"John","age":30,"active":true,"scores":[85,92,78]}
78
79
// Pretty printing with 2 spaces
80
val pretty2 = json.spaces2
81
// Result:
82
// {
83
// "name" : "John",
84
// "age" : 30,
85
// "active" : true,
86
// "scores" : [
87
// 85,
88
// 92,
89
// 78
90
// ]
91
// }
92
93
// Pretty printing with 4 spaces
94
val pretty4 = json.spaces4
95
// Result:
96
// {
97
// "name" : "John",
98
// "age" : 30,
99
// "active" : true,
100
// "scores" : [
101
// 85,
102
// 92,
103
// 78
104
// ]
105
// }
106
```
107
108
### Custom Printer Configuration
109
110
```scala
111
import io.circe._
112
113
val json = Json.obj(
114
"name" -> Json.fromString("John"),
115
"email" -> Json.Null,
116
"age" -> Json.fromInt(30)
117
)
118
119
// Custom printer that drops null values
120
val customPrinter = Printer(
121
dropNullValues = true,
122
indent = " ",
123
lbraceRight = "\n",
124
rbraceLeft = "\n",
125
arrayCommaRight = "\n",
126
objectCommaRight = "\n",
127
colonLeft = " ",
128
colonRight = " "
129
)
130
131
val result = customPrinter.print(json)
132
// Result:
133
// {
134
// "name" : "John",
135
// "age" : 30
136
// }
137
// Note: "email": null is omitted
138
```
139
140
### Sorted Keys
141
142
```scala
143
import io.circe._
144
145
val json = Json.obj(
146
"zebra" -> Json.fromString("animal"),
147
"apple" -> Json.fromString("fruit"),
148
"banana" -> Json.fromString("fruit"),
149
"ant" -> Json.fromString("insect")
150
)
151
152
// Default order (insertion order)
153
val unsorted = json.spaces2
154
// Result:
155
// {
156
// "zebra" : "animal",
157
// "apple" : "fruit",
158
// "banana" : "fruit",
159
// "ant" : "insect"
160
// }
161
162
// Sorted keys
163
val sorted = json.printWith(Printer.spaces2SortKeys)
164
// Result:
165
// {
166
// "ant" : "insect",
167
// "apple" : "fruit",
168
// "banana" : "fruit",
169
// "zebra" : "animal"
170
// }
171
172
// Or using withSortedKeys
173
val sortedCustom = Printer.spaces4.withSortedKeys.print(json)
174
```
175
176
### Performance Optimizations
177
178
```scala
179
import io.circe._
180
import java.nio.charset.StandardCharsets
181
182
val largeJson = Json.obj(
183
"data" -> Json.arr((1 to 1000).map(i => Json.obj(
184
"id" -> Json.fromInt(i),
185
"value" -> Json.fromString(s"item-$i")
186
)): _*)
187
)
188
189
// Reuse writers for better performance in multi-threaded scenarios
190
val optimizedPrinter = Printer(
191
dropNullValues = false,
192
indent = "",
193
reuseWriters = true,
194
predictSize = true
195
)
196
197
// String output
198
val jsonString = optimizedPrinter.print(largeJson)
199
200
// Binary output for network transmission
201
val byteBuffer = optimizedPrinter.printToByteBuffer(largeJson, StandardCharsets.UTF_8)
202
val bytes = new Array[Byte](byteBuffer.remaining())
203
byteBuffer.get(bytes)
204
```
205
206
### Unicode Handling
207
208
```scala
209
import io.circe._
210
211
val json = Json.obj(
212
"message" -> Json.fromString("Hello δΈη! π"),
213
"emoji" -> Json.fromString("π π π π")
214
)
215
216
// Default: preserves Unicode characters
217
val withUnicode = json.noSpaces
218
// Result: {"message":"Hello δΈη! π","emoji":"π π π π"}
219
220
// Escape non-ASCII characters
221
val unicodeEscaped = Printer(
222
dropNullValues = false,
223
indent = "",
224
escapeNonAscii = true
225
).print(json)
226
// Result: {"message":"Hello \\u4e16\\u754c! \\ud83c\\udf0d","emoji":"\\ud83d\\ude00 \\ud83d\\ude03 \\ud83d\\ude04 \\ud83d\\ude01"}
227
```
228
229
### Custom Formatting
230
231
```scala
232
import io.circe._
233
234
val json = Json.obj(
235
"users" -> Json.arr(
236
Json.obj("name" -> Json.fromString("Alice")),
237
Json.obj("name" -> Json.fromString("Bob"))
238
)
239
)
240
241
// Minimal spacing
242
val minimal = Printer(
243
dropNullValues = false,
244
indent = "",
245
colonRight = " ",
246
arrayCommaRight = " ",
247
objectCommaRight = " "
248
).print(json)
249
// Result: {"users": [{"name": "Alice"}, {"name": "Bob"}]}
250
251
// Custom indentation with tabs
252
val tabIndented = Printer(
253
dropNullValues = false,
254
indent = "\t",
255
lbraceRight = "\n",
256
rbraceLeft = "\n",
257
lbracketRight = "\n",
258
rbracketLeft = "\n",
259
arrayCommaRight = "\n",
260
objectCommaRight = "\n",
261
colonLeft = " ",
262
colonRight = " "
263
).print(json)
264
// Uses tabs for indentation
265
266
// No spacing around arrays
267
val compactArrays = Printer(
268
dropNullValues = false,
269
indent = " ",
270
lbraceRight = "\n",
271
rbraceLeft = "\n",
272
objectCommaRight = "\n",
273
colonLeft = " ",
274
colonRight = " "
275
// Arrays remain compact: no lbracketRight/rbracketLeft
276
).print(json)
277
```
278
279
### Working with Appendable
280
281
```scala
282
import io.circe._
283
import java.io.StringWriter
284
285
val json = Json.obj("message" -> Json.fromString("Hello World"))
286
287
// Write to StringBuilder
288
val builder = new StringBuilder()
289
Printer.noSpaces.unsafePrintToAppendable(json, builder)
290
val result1 = builder.toString
291
292
// Write to StringWriter
293
val writer = new StringWriter()
294
Printer.spaces2.unsafePrintToAppendable(json, writer)
295
val result2 = writer.toString
296
297
// Write to any Appendable
298
class CustomAppendable extends Appendable {
299
private val buffer = new StringBuilder()
300
301
def append(csq: CharSequence): Appendable = {
302
buffer.append(csq)
303
this
304
}
305
306
def append(csq: CharSequence, start: Int, end: Int): Appendable = {
307
buffer.append(csq, start, end)
308
this
309
}
310
311
def append(c: Char): Appendable = {
312
buffer.append(c)
313
this
314
}
315
316
def getResult: String = buffer.toString
317
}
318
319
val custom = new CustomAppendable()
320
Printer.noSpaces.unsafePrintToAppendable(json, custom)
321
val result3 = custom.getResult
322
```
323
324
### Binary Output
325
326
```scala
327
import io.circe._
328
import java.nio.charset.{Charset, StandardCharsets}
329
import java.nio.ByteBuffer
330
331
val json = Json.obj(
332
"text" -> Json.fromString("Hello"),
333
"number" -> Json.fromInt(42)
334
)
335
336
// UTF-8 output (default)
337
val utf8Buffer = Printer.noSpaces.printToByteBuffer(json)
338
val utf8Bytes = new Array[Byte](utf8Buffer.remaining())
339
utf8Buffer.get(utf8Bytes)
340
val utf8String = new String(utf8Bytes, StandardCharsets.UTF_8)
341
342
// UTF-16 output
343
val utf16Buffer = Printer.noSpaces.printToByteBuffer(json, StandardCharsets.UTF_16)
344
val utf16Bytes = new Array[Byte](utf16Buffer.remaining())
345
utf16Buffer.get(utf16Bytes)
346
val utf16String = new String(utf16Bytes, StandardCharsets.UTF_16)
347
348
// ASCII with escaped Unicode
349
val asciiPrinter = Printer(
350
dropNullValues = false,
351
indent = "",
352
escapeNonAscii = true
353
)
354
val asciiBuffer = asciiPrinter.printToByteBuffer(json, StandardCharsets.US_ASCII)
355
```
356
357
### Error Handling in Printing
358
359
```scala
360
import io.circe._
361
362
// Printing generally doesn't fail, but here are edge cases
363
val problematicJson = Json.fromDouble(Double.NaN)
364
365
problematicJson match {
366
case Some(json) =>
367
val printed = json.noSpaces
368
println(s"Printed: $printed")
369
case None =>
370
println("Cannot create JSON from NaN")
371
}
372
373
// Infinite values
374
val infiniteJson = Json.fromDouble(Double.PositiveInfinity)
375
infiniteJson match {
376
case Some(json) =>
377
val printed = json.noSpaces
378
println(s"Printed: $printed") // "null"
379
case None =>
380
println("Cannot create JSON from infinite value")
381
}
382
```
383
384
### Streaming and Large JSON
385
386
```scala
387
import io.circe._
388
import java.io.{FileWriter, BufferedWriter}
389
390
// For very large JSON, use unsafePrintToAppendable with a buffered writer
391
val largeJson = Json.obj(
392
"items" -> Json.arr(
393
(1 to 100000).map(i => Json.obj(
394
"id" -> Json.fromInt(i),
395
"data" -> Json.fromString(s"data-$i")
396
)): _*
397
)
398
)
399
400
// Write directly to file
401
val fileWriter = new BufferedWriter(new FileWriter("large.json"))
402
try {
403
Printer.noSpaces.unsafePrintToAppendable(largeJson, fileWriter)
404
} finally {
405
fileWriter.close()
406
}
407
408
// Or use ByteBuffer for binary output
409
val buffer = Printer.noSpaces.printToByteBuffer(largeJson)
410
// Can write buffer to FileChannel, SocketChannel, etc.
411
```
412
413
### Printer Performance Comparison
414
415
```scala
416
import io.circe._
417
418
val testJson = Json.obj(
419
"data" -> Json.arr((1 to 1000).map(Json.fromInt): _*)
420
)
421
422
// Compact printing (fastest)
423
val compact = Printer.noSpaces
424
val compactResult = compact.print(testJson)
425
426
// Pretty printing (slower due to whitespace)
427
val pretty = Printer.spaces2
428
val prettyResult = pretty.print(testJson)
429
430
// With optimizations (fastest for repeated use)
431
val optimized = Printer(
432
dropNullValues = false,
433
indent = "",
434
reuseWriters = true,
435
predictSize = true
436
)
437
val optimizedResult = optimized.print(testJson)
438
439
// With sorted keys (slowest due to sorting)
440
val sorted = Printer.noSpacesSortKeys
441
val sortedResult = sorted.print(testJson)
442
```