0
# JSON Integration
1
2
Direct integration with ujson for working with JSON AST and value types. This provides seamless interoperability between uPickle serialization and ujson manipulation.
3
4
## Capabilities
5
6
### JSON AST Writing
7
8
Writes Scala values directly to ujson AST types for manipulation and processing.
9
10
```scala { .api }
11
/**
12
* Write the given Scala value as a JSON struct (ujson.Value)
13
* @param t Value to serialize
14
* @return ujson.Value representation
15
*/
16
def writeJs[T: Writer](t: T): ujson.Value
17
```
18
19
**Usage Examples:**
20
21
```scala
22
import upickle.default._
23
import ujson._
24
25
case class Person(name: String, age: Int)
26
val person = Person("Alice", 30)
27
28
// Convert to ujson AST
29
val jsonAst = writeJs(person)
30
// Result: ujson.Obj("name" -> ujson.Str("Alice"), "age" -> ujson.Num(30))
31
32
// Manipulate JSON AST
33
val modifiedAst = jsonAst.obj("age") = ujson.Num(31)
34
35
// Convert back to string
36
val jsonString = jsonAst.toString
37
// Result: {"name":"Alice","age":30}
38
39
// Pretty print
40
val prettyJson = ujson.write(jsonAst, indent = 2)
41
```
42
43
### ujson.Value Reader and Writer
44
45
Built-in support for reading and writing ujson.Value and its subtypes.
46
47
```scala { .api }
48
/**
49
* Reader for generic ujson.Value
50
*/
51
implicit def JsValueR: Reader[ujson.Value]
52
53
/**
54
* Writer for generic ujson.Value
55
*/
56
implicit def JsValueW: Writer[ujson.Value]
57
```
58
59
**Usage Examples:**
60
61
```scala
62
import upickle.default._
63
import ujson._
64
65
// Reading ujson.Value from JSON string
66
val jsonStr = """{"name":"Alice","items":[1,2,3],"active":true}"""
67
val jsonValue = read[ujson.Value](jsonStr)
68
69
// Working with the parsed value
70
val name = jsonValue("name").str // "Alice"
71
val items = jsonValue("items").arr.map(_.num.toInt) // List(1, 2, 3)
72
val active = jsonValue("active").bool // true
73
74
// Writing ujson.Value back to JSON
75
val backToJson = write(jsonValue)
76
```
77
78
### Specific ujson Type Readers
79
80
Readers for specific ujson value types with type safety.
81
82
```scala { .api }
83
implicit def JsObjR: Reader[ujson.Obj]
84
implicit def JsArrR: Reader[ujson.Arr]
85
implicit def JsStrR: Reader[ujson.Str]
86
implicit def JsNumR: Reader[ujson.Num]
87
implicit def JsBoolR: Reader[ujson.Bool]
88
implicit def JsTrueR: Reader[ujson.True.type]
89
implicit def JsFalseR: Reader[ujson.False.type]
90
implicit def JsNullR: Reader[ujson.Null.type]
91
```
92
93
**Usage Examples:**
94
95
```scala
96
import upickle.default._
97
import ujson._
98
99
// Read specific JSON types
100
val objValue = read[ujson.Obj]("""{"key":"value"}""")
101
val arrValue = read[ujson.Arr]("""[1,2,3]""")
102
val strValue = read[ujson.Str](""""hello"""")
103
val numValue = read[ujson.Num]("""42.5""")
104
val boolValue = read[ujson.Bool]("""true""")
105
val nullValue = read[ujson.Null.type]("""null""")
106
107
// Type-safe access
108
val keyValue = objValue.value("key") // ujson.Str
109
val firstItem = arrValue.value(0) // ujson.Num
110
val stringContent = strValue.value // String
111
val numberContent = numValue.value // Double
112
```
113
114
### Specific ujson Type Writers
115
116
Writers for specific ujson value types.
117
118
```scala { .api }
119
implicit def JsObjW: Writer[ujson.Obj]
120
implicit def JsArrW: Writer[ujson.Arr]
121
implicit def JsStrW: Writer[ujson.Str]
122
implicit def JsNumW: Writer[ujson.Num]
123
implicit def JsBoolW: Writer[ujson.Bool]
124
implicit def JsTrueW: Writer[ujson.True.type]
125
implicit def JsFalseW: Writer[ujson.False.type]
126
implicit def JsNullW: Writer[ujson.Null.type]
127
```
128
129
**Usage Examples:**
130
131
```scala
132
import upickle.default._
133
import ujson._
134
135
// Create ujson values
136
val obj = ujson.Obj("name" -> ujson.Str("Alice"), "age" -> ujson.Num(30))
137
val arr = ujson.Arr(ujson.Num(1), ujson.Num(2), ujson.Num(3))
138
139
// Write specific types back to JSON
140
val objJson = write(obj) // {"name":"Alice","age":30}
141
val arrJson = write(arr) // [1,2,3]
142
val strJson = write(ujson.Str("hello")) // "hello"
143
val numJson = write(ujson.Num(42)) // 42
144
```
145
146
### JSON Transformation Patterns
147
148
Common patterns for working with JSON data using ujson integration.
149
150
**Usage Examples:**
151
152
```scala
153
import upickle.default._
154
import ujson._
155
156
// Transform JSON structure
157
def transformJson(input: String): String = {
158
val json = read[ujson.Value](input)
159
160
// Add timestamp
161
json("timestamp") = ujson.Str(java.time.Instant.now().toString)
162
163
// Transform nested objects
164
json("users").arr.foreach { user =>
165
user("id") = ujson.Str(java.util.UUID.randomUUID().toString)
166
}
167
168
write(json)
169
}
170
171
// Merge JSON objects
172
def mergeJson(json1: String, json2: String): String = {
173
val obj1 = read[ujson.Obj](json1)
174
val obj2 = read[ujson.Obj](json2)
175
176
// Merge objects
177
obj2.value.foreach { case (key, value) =>
178
obj1(key) = value
179
}
180
181
write(obj1)
182
}
183
184
// Filter JSON arrays
185
def filterJsonArray(input: String, predicate: ujson.Value => Boolean): String = {
186
val json = read[ujson.Value](input)
187
188
json match {
189
case arr: ujson.Arr =>
190
val filtered = ujson.Arr(arr.value.filter(predicate): _*)
191
write(filtered)
192
case _ => input
193
}
194
}
195
196
// Usage examples
197
val original = """{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}"""
198
val transformed = transformJson(original)
199
200
val json1 = """{"a":1,"b":2}"""
201
val json2 = """{"c":3,"d":4}"""
202
val merged = mergeJson(json1, json2) // {"a":1,"b":2,"c":3,"d":4}
203
204
val arrayJson = """[1,2,3,4,5]"""
205
val filtered = filterJsonArray(arrayJson, _.num > 3) // [4,5]
206
```
207
208
### Direct ujson Operations
209
210
Core ujson module functions for JSON processing and manipulation.
211
212
```scala { .api }
213
/**
214
* Read JSON input into ujson.Value AST
215
* @param s JSON input (string, InputStream, etc.)
216
* @param trace Enable tracing for debugging
217
* @return ujson.Value AST representation
218
*/
219
def ujson.read(s: ujson.Readable, trace: Boolean = false): ujson.Value
220
221
/**
222
* Write ujson.Value to JSON string
223
* @param t ujson.Value to serialize
224
* @param indent Indentation level (-1 for compact, >=0 for pretty printing)
225
* @param escapeUnicode Whether to escape unicode characters
226
* @param sortKeys Whether to sort object keys alphabetically
227
* @return JSON string representation
228
*/
229
def ujson.write(t: ujson.Value, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): String
230
231
/**
232
* Write ujson.Value directly to Writer
233
*/
234
def ujson.writeTo(t: ujson.Value, out: java.io.Writer, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Unit
235
236
/**
237
* Write ujson.Value to OutputStream as UTF-8 bytes
238
*/
239
def ujson.writeToOutputStream(t: ujson.Value, out: java.io.OutputStream, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Unit
240
241
/**
242
* Write ujson.Value to byte array
243
*/
244
def ujson.writeToByteArray(t: ujson.Value, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Array[Byte]
245
```
246
247
**Usage Examples:**
248
249
```scala
250
import ujson._
251
252
// Direct ujson operations
253
val jsonString = """{"name":"Alice","scores":[85,90,88],"active":true}"""
254
255
// Parse to ujson.Value
256
val jsonAst = ujson.read(jsonString)
257
258
// Access values
259
val name = jsonAst("name").str // "Alice"
260
val scores = jsonAst("scores").arr.map(_.num.toInt) // Vector(85, 90, 88)
261
val active = jsonAst("active").bool // true
262
263
// Modify the AST
264
jsonAst("active") = ujson.Bool(false)
265
jsonAst("lastLogin") = ujson.Str("2024-01-15")
266
267
// Write back to JSON
268
val modifiedJson = ujson.write(jsonAst, indent = 2)
269
270
// Write to file
271
val fileWriter = new java.io.FileWriter("output.json")
272
try {
273
ujson.writeTo(jsonAst, fileWriter, indent = 2)
274
} finally {
275
fileWriter.close()
276
}
277
278
// Write to byte array for network transmission
279
val jsonBytes = ujson.writeToByteArray(jsonAst)
280
```
281
282
### JSON Validation
283
284
Validate JSON input without parsing to check syntax correctness.
285
286
```scala { .api }
287
/**
288
* Validate JSON syntax without full parsing
289
* @param s JSON input to validate
290
* @throws ujson.ParseException if invalid JSON
291
*/
292
def ujson.validate(s: ujson.Readable): Unit
293
```
294
295
**Usage Examples:**
296
297
```scala
298
import ujson._
299
300
// Validate JSON strings
301
try {
302
ujson.validate("""{"valid": "json"}""")
303
println("Valid JSON")
304
} catch {
305
case e: ujson.ParseException => println(s"Invalid JSON: ${e.getMessage}")
306
}
307
308
// Validate JSON from file
309
try {
310
val fileContent = scala.io.Source.fromFile("data.json").mkString
311
ujson.validate(fileContent)
312
println("File contains valid JSON")
313
} catch {
314
case e: ujson.ParseException => println(s"Invalid JSON in file: ${e.getMessage}")
315
}
316
317
// Use before expensive parsing operations
318
def safeParseJson(input: String): Option[ujson.Value] = {
319
try {
320
ujson.validate(input)
321
Some(ujson.read(input))
322
} catch {
323
case _: ujson.ParseException => None
324
}
325
}
326
```
327
328
### JSON Reformatting
329
330
Reformat JSON with different styles without full object model conversion.
331
332
```scala { .api }
333
/**
334
* Reformat JSON input with new formatting options
335
* @param s JSON input to reformat
336
* @param indent Indentation level (-1 for compact, >=0 for pretty printing)
337
* @param escapeUnicode Whether to escape unicode characters
338
* @param sortKeys Whether to sort object keys alphabetically
339
* @return Reformatted JSON string
340
*/
341
def ujson.reformat(s: ujson.Readable, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): String
342
343
/**
344
* Reformat JSON directly to Writer
345
*/
346
def ujson.reformatTo(s: ujson.Readable, out: java.io.Writer, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Unit
347
348
/**
349
* Reformat JSON to OutputStream
350
*/
351
def ujson.reformatToOutputStream(s: ujson.Readable, out: java.io.OutputStream, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Unit
352
353
/**
354
* Reformat JSON to byte array
355
*/
356
def ujson.reformatToByteArray(s: ujson.Readable, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Array[Byte]
357
```
358
359
**Usage Examples:**
360
361
```scala
362
import ujson._
363
364
// Compact JSON to pretty-printed
365
val compactJson = """{"name":"Alice","data":{"scores":[85,90,88],"active":true}}"""
366
val prettyJson = ujson.reformat(compactJson, indent = 2)
367
println(prettyJson)
368
// Result:
369
// {
370
// "name": "Alice",
371
// "data": {
372
// "scores": [85, 90, 88],
373
// "active": true
374
// }
375
// }
376
377
// Sort keys alphabetically
378
val sortedJson = ujson.reformat(compactJson, indent = 2, sortKeys = true)
379
380
// Escape unicode characters
381
val unicodeJson = """{"message":"Hello 世界"}"""
382
val escapedJson = ujson.reformat(unicodeJson, escapeUnicode = true)
383
// Result: {"message":"Hello \\u4e16\\u754c"}
384
385
// Reformat large file without loading into memory
386
val inputFile = new java.io.FileInputStream("input.json")
387
val outputFile = new java.io.FileOutputStream("output.json")
388
try {
389
ujson.reformatToOutputStream(inputFile, outputFile, indent = 2, sortKeys = true)
390
} finally {
391
inputFile.close()
392
outputFile.close()
393
}
394
```
395
396
### Low-level JSON Processing
397
398
Advanced functions for custom JSON processing workflows.
399
400
```scala { .api }
401
/**
402
* Transform JSON using custom visitor
403
* @param t JSON input
404
* @param v Custom visitor for processing
405
* @param sortKeys Whether to sort keys during processing
406
*/
407
def ujson.transform[T](t: ujson.Readable, v: upickle.core.Visitor[_, T], sortKeys: Boolean = false): T
408
409
/**
410
* Copy ujson.Value creating a deep clone
411
* @param t ujson.Value to copy
412
* @return Deep copy of the input
413
*/
414
def ujson.copy(t: ujson.Value): ujson.Value
415
```
416
417
**Usage Examples:**
418
419
```scala
420
import ujson._
421
import upickle.core._
422
423
// Custom visitor to count JSON elements
424
class CountingVisitor extends Visitor[Any, Int] {
425
private var count = 0
426
427
override def visitArray(length: Int, index: Int) = new ArrVisitor[Any, Int] {
428
def subVisitor = CountingVisitor.this
429
def visitValue(v: Any, index: Int): Unit = count += 1
430
def visitEnd(index: Int) = count
431
}
432
433
override def visitObject(length: Int, jsonableKeys: Boolean, index: Int) = new ObjVisitor[Any, Int] {
434
def subVisitor = CountingVisitor.this
435
def visitKey(index: Int) = CountingVisitor.this
436
def visitKeyValue(s: Any): Unit = {}
437
def visitValue(v: Any, index: Int): Unit = count += 1
438
def visitEnd(index: Int) = count
439
}
440
441
override def visitString(s: CharSequence, index: Int) = { count += 1; count }
442
override def visitNum(d: Double, decIndex: Int, expIndex: Int, index: Int) = { count += 1; count }
443
override def visitBool(b: Boolean, index: Int) = { count += 1; count }
444
override def visitNull(index: Int) = { count += 1; count }
445
}
446
447
// Count elements in JSON
448
val jsonString = """{"users":[{"name":"Alice"},{"name":"Bob"}],"count":2}"""
449
val elementCount = ujson.transform(jsonString, new CountingVisitor())
450
println(s"JSON contains $elementCount elements")
451
452
// Deep copy ujson.Value
453
val original = ujson.Obj("data" -> ujson.Arr(ujson.Num(1), ujson.Num(2)))
454
val copied = ujson.copy(original)
455
456
// Modify copy without affecting original
457
copied("data").arr += ujson.Num(3)
458
println(ujson.write(original)) // {"data":[1,2]}
459
println(ujson.write(copied)) // {"data":[1,2,3]}
460
```
461
462
### Mixed Type Serialization
463
464
Using ujson integration for heterogeneous data structures.
465
466
**Usage Examples:**
467
468
```scala
469
import upickle.default._
470
import ujson._
471
472
// Working with mixed data types
473
case class MixedData(
474
metadata: ujson.Obj,
475
payload: ujson.Value,
476
timestamp: Long
477
)
478
479
implicit val mixedDataRW: ReadWriter[MixedData] = macroRW
480
481
val mixedData = MixedData(
482
metadata = ujson.Obj("version" -> ujson.Str("1.0"), "source" -> ujson.Str("api")),
483
payload = ujson.Arr(ujson.Num(1), ujson.Str("test"), ujson.Bool(true)),
484
timestamp = System.currentTimeMillis()
485
)
486
487
val json = write(mixedData)
488
val parsed = read[MixedData](json)
489
490
// Dynamic JSON construction
491
def buildDynamicJson(data: Map[String, Any]): ujson.Value = {
492
val obj = ujson.Obj()
493
494
data.foreach {
495
case (key, value: String) => obj(key) = ujson.Str(value)
496
case (key, value: Int) => obj(key) = ujson.Num(value)
497
case (key, value: Double) => obj(key) = ujson.Num(value)
498
case (key, value: Boolean) => obj(key) = ujson.Bool(value)
499
case (key, value: List[_]) => obj(key) = ujson.Arr(value.map(writeJs): _*)
500
case (key, null) => obj(key) = ujson.Null
501
case (key, value) => obj(key) = writeJs(value)
502
}
503
504
obj
505
}
506
507
val dynamicData = Map(
508
"name" -> "Alice",
509
"age" -> 30,
510
"active" -> true,
511
"scores" -> List(85.5, 90.0, 88.5)
512
)
513
514
val dynamicJson = buildDynamicJson(dynamicData)
515
val jsonString = write(dynamicJson)
516
```
517
518
## Types
519
520
```scala { .api }
521
/**
522
* ujson.Value is the base type for all JSON AST nodes
523
*/
524
sealed trait ujson.Value {
525
def apply(s: ujson.Selector): ujson.Value
526
def update(s: ujson.Selector, v: ujson.Value): Unit
527
}
528
529
/**
530
* Specific ujson value types
531
*/
532
case class ujson.Obj(value: mutable.LinkedHashMap[String, ujson.Value]) extends ujson.Value
533
case class ujson.Arr(value: mutable.Buffer[ujson.Value]) extends ujson.Value
534
case class ujson.Str(value: String) extends ujson.Value
535
case class ujson.Num(value: Double) extends ujson.Value
536
case class ujson.Bool(value: Boolean) extends ujson.Value
537
case object ujson.True extends ujson.Bool(true)
538
case object ujson.False extends ujson.Bool(false)
539
case object ujson.Null extends ujson.Value
540
541
/**
542
* ujson.Readable represents sources that can provide JSON data
543
*/
544
trait ujson.Readable {
545
def transform[T](f: ujson.Visitor[_, T]): T
546
}
547
```