Direct integration with ujson for working with JSON AST and value types. This provides seamless interoperability between uPickle serialization and ujson manipulation.
Writes Scala values directly to ujson AST types for manipulation and processing.
/**
* Write the given Scala value as a JSON struct (ujson.Value)
* @param t Value to serialize
* @return ujson.Value representation
*/
def writeJs[T: Writer](t: T): ujson.ValueUsage Examples:
import upickle.default._
import ujson._
case class Person(name: String, age: Int)
val person = Person("Alice", 30)
// Convert to ujson AST
val jsonAst = writeJs(person)
// Result: ujson.Obj("name" -> ujson.Str("Alice"), "age" -> ujson.Num(30))
// Manipulate JSON AST
val modifiedAst = jsonAst.obj("age") = ujson.Num(31)
// Convert back to string
val jsonString = jsonAst.toString
// Result: {"name":"Alice","age":30}
// Pretty print
val prettyJson = ujson.write(jsonAst, indent = 2)Built-in support for reading and writing ujson.Value and its subtypes.
/**
* Reader for generic ujson.Value
*/
implicit def JsValueR: Reader[ujson.Value]
/**
* Writer for generic ujson.Value
*/
implicit def JsValueW: Writer[ujson.Value]Usage Examples:
import upickle.default._
import ujson._
// Reading ujson.Value from JSON string
val jsonStr = """{"name":"Alice","items":[1,2,3],"active":true}"""
val jsonValue = read[ujson.Value](jsonStr)
// Working with the parsed value
val name = jsonValue("name").str // "Alice"
val items = jsonValue("items").arr.map(_.num.toInt) // List(1, 2, 3)
val active = jsonValue("active").bool // true
// Writing ujson.Value back to JSON
val backToJson = write(jsonValue)Readers for specific ujson value types with type safety.
implicit def JsObjR: Reader[ujson.Obj]
implicit def JsArrR: Reader[ujson.Arr]
implicit def JsStrR: Reader[ujson.Str]
implicit def JsNumR: Reader[ujson.Num]
implicit def JsBoolR: Reader[ujson.Bool]
implicit def JsTrueR: Reader[ujson.True.type]
implicit def JsFalseR: Reader[ujson.False.type]
implicit def JsNullR: Reader[ujson.Null.type]Usage Examples:
import upickle.default._
import ujson._
// Read specific JSON types
val objValue = read[ujson.Obj]("""{"key":"value"}""")
val arrValue = read[ujson.Arr]("""[1,2,3]""")
val strValue = read[ujson.Str](""""hello"""")
val numValue = read[ujson.Num]("""42.5""")
val boolValue = read[ujson.Bool]("""true""")
val nullValue = read[ujson.Null.type]("""null""")
// Type-safe access
val keyValue = objValue.value("key") // ujson.Str
val firstItem = arrValue.value(0) // ujson.Num
val stringContent = strValue.value // String
val numberContent = numValue.value // DoubleWriters for specific ujson value types.
implicit def JsObjW: Writer[ujson.Obj]
implicit def JsArrW: Writer[ujson.Arr]
implicit def JsStrW: Writer[ujson.Str]
implicit def JsNumW: Writer[ujson.Num]
implicit def JsBoolW: Writer[ujson.Bool]
implicit def JsTrueW: Writer[ujson.True.type]
implicit def JsFalseW: Writer[ujson.False.type]
implicit def JsNullW: Writer[ujson.Null.type]Usage Examples:
import upickle.default._
import ujson._
// Create ujson values
val obj = ujson.Obj("name" -> ujson.Str("Alice"), "age" -> ujson.Num(30))
val arr = ujson.Arr(ujson.Num(1), ujson.Num(2), ujson.Num(3))
// Write specific types back to JSON
val objJson = write(obj) // {"name":"Alice","age":30}
val arrJson = write(arr) // [1,2,3]
val strJson = write(ujson.Str("hello")) // "hello"
val numJson = write(ujson.Num(42)) // 42Common patterns for working with JSON data using ujson integration.
Usage Examples:
import upickle.default._
import ujson._
// Transform JSON structure
def transformJson(input: String): String = {
val json = read[ujson.Value](input)
// Add timestamp
json("timestamp") = ujson.Str(java.time.Instant.now().toString)
// Transform nested objects
json("users").arr.foreach { user =>
user("id") = ujson.Str(java.util.UUID.randomUUID().toString)
}
write(json)
}
// Merge JSON objects
def mergeJson(json1: String, json2: String): String = {
val obj1 = read[ujson.Obj](json1)
val obj2 = read[ujson.Obj](json2)
// Merge objects
obj2.value.foreach { case (key, value) =>
obj1(key) = value
}
write(obj1)
}
// Filter JSON arrays
def filterJsonArray(input: String, predicate: ujson.Value => Boolean): String = {
val json = read[ujson.Value](input)
json match {
case arr: ujson.Arr =>
val filtered = ujson.Arr(arr.value.filter(predicate): _*)
write(filtered)
case _ => input
}
}
// Usage examples
val original = """{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}"""
val transformed = transformJson(original)
val json1 = """{"a":1,"b":2}"""
val json2 = """{"c":3,"d":4}"""
val merged = mergeJson(json1, json2) // {"a":1,"b":2,"c":3,"d":4}
val arrayJson = """[1,2,3,4,5]"""
val filtered = filterJsonArray(arrayJson, _.num > 3) // [4,5]Core ujson module functions for JSON processing and manipulation.
/**
* Read JSON input into ujson.Value AST
* @param s JSON input (string, InputStream, etc.)
* @param trace Enable tracing for debugging
* @return ujson.Value AST representation
*/
def ujson.read(s: ujson.Readable, trace: Boolean = false): ujson.Value
/**
* Write ujson.Value to JSON string
* @param t ujson.Value to serialize
* @param indent Indentation level (-1 for compact, >=0 for pretty printing)
* @param escapeUnicode Whether to escape unicode characters
* @param sortKeys Whether to sort object keys alphabetically
* @return JSON string representation
*/
def ujson.write(t: ujson.Value, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): String
/**
* Write ujson.Value directly to Writer
*/
def ujson.writeTo(t: ujson.Value, out: java.io.Writer, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Unit
/**
* Write ujson.Value to OutputStream as UTF-8 bytes
*/
def ujson.writeToOutputStream(t: ujson.Value, out: java.io.OutputStream, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Unit
/**
* Write ujson.Value to byte array
*/
def ujson.writeToByteArray(t: ujson.Value, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Array[Byte]Usage Examples:
import ujson._
// Direct ujson operations
val jsonString = """{"name":"Alice","scores":[85,90,88],"active":true}"""
// Parse to ujson.Value
val jsonAst = ujson.read(jsonString)
// Access values
val name = jsonAst("name").str // "Alice"
val scores = jsonAst("scores").arr.map(_.num.toInt) // Vector(85, 90, 88)
val active = jsonAst("active").bool // true
// Modify the AST
jsonAst("active") = ujson.Bool(false)
jsonAst("lastLogin") = ujson.Str("2024-01-15")
// Write back to JSON
val modifiedJson = ujson.write(jsonAst, indent = 2)
// Write to file
val fileWriter = new java.io.FileWriter("output.json")
try {
ujson.writeTo(jsonAst, fileWriter, indent = 2)
} finally {
fileWriter.close()
}
// Write to byte array for network transmission
val jsonBytes = ujson.writeToByteArray(jsonAst)Validate JSON input without parsing to check syntax correctness.
/**
* Validate JSON syntax without full parsing
* @param s JSON input to validate
* @throws ujson.ParseException if invalid JSON
*/
def ujson.validate(s: ujson.Readable): UnitUsage Examples:
import ujson._
// Validate JSON strings
try {
ujson.validate("""{"valid": "json"}""")
println("Valid JSON")
} catch {
case e: ujson.ParseException => println(s"Invalid JSON: ${e.getMessage}")
}
// Validate JSON from file
try {
val fileContent = scala.io.Source.fromFile("data.json").mkString
ujson.validate(fileContent)
println("File contains valid JSON")
} catch {
case e: ujson.ParseException => println(s"Invalid JSON in file: ${e.getMessage}")
}
// Use before expensive parsing operations
def safeParseJson(input: String): Option[ujson.Value] = {
try {
ujson.validate(input)
Some(ujson.read(input))
} catch {
case _: ujson.ParseException => None
}
}Reformat JSON with different styles without full object model conversion.
/**
* Reformat JSON input with new formatting options
* @param s JSON input to reformat
* @param indent Indentation level (-1 for compact, >=0 for pretty printing)
* @param escapeUnicode Whether to escape unicode characters
* @param sortKeys Whether to sort object keys alphabetically
* @return Reformatted JSON string
*/
def ujson.reformat(s: ujson.Readable, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): String
/**
* Reformat JSON directly to Writer
*/
def ujson.reformatTo(s: ujson.Readable, out: java.io.Writer, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Unit
/**
* Reformat JSON to OutputStream
*/
def ujson.reformatToOutputStream(s: ujson.Readable, out: java.io.OutputStream, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Unit
/**
* Reformat JSON to byte array
*/
def ujson.reformatToByteArray(s: ujson.Readable, indent: Int = -1, escapeUnicode: Boolean = false, sortKeys: Boolean = false): Array[Byte]Usage Examples:
import ujson._
// Compact JSON to pretty-printed
val compactJson = """{"name":"Alice","data":{"scores":[85,90,88],"active":true}}"""
val prettyJson = ujson.reformat(compactJson, indent = 2)
println(prettyJson)
// Result:
// {
// "name": "Alice",
// "data": {
// "scores": [85, 90, 88],
// "active": true
// }
// }
// Sort keys alphabetically
val sortedJson = ujson.reformat(compactJson, indent = 2, sortKeys = true)
// Escape unicode characters
val unicodeJson = """{"message":"Hello 世界"}"""
val escapedJson = ujson.reformat(unicodeJson, escapeUnicode = true)
// Result: {"message":"Hello \\u4e16\\u754c"}
// Reformat large file without loading into memory
val inputFile = new java.io.FileInputStream("input.json")
val outputFile = new java.io.FileOutputStream("output.json")
try {
ujson.reformatToOutputStream(inputFile, outputFile, indent = 2, sortKeys = true)
} finally {
inputFile.close()
outputFile.close()
}Advanced functions for custom JSON processing workflows.
/**
* Transform JSON using custom visitor
* @param t JSON input
* @param v Custom visitor for processing
* @param sortKeys Whether to sort keys during processing
*/
def ujson.transform[T](t: ujson.Readable, v: upickle.core.Visitor[_, T], sortKeys: Boolean = false): T
/**
* Copy ujson.Value creating a deep clone
* @param t ujson.Value to copy
* @return Deep copy of the input
*/
def ujson.copy(t: ujson.Value): ujson.ValueUsage Examples:
import ujson._
import upickle.core._
// Custom visitor to count JSON elements
class CountingVisitor extends Visitor[Any, Int] {
private var count = 0
override def visitArray(length: Int, index: Int) = new ArrVisitor[Any, Int] {
def subVisitor = CountingVisitor.this
def visitValue(v: Any, index: Int): Unit = count += 1
def visitEnd(index: Int) = count
}
override def visitObject(length: Int, jsonableKeys: Boolean, index: Int) = new ObjVisitor[Any, Int] {
def subVisitor = CountingVisitor.this
def visitKey(index: Int) = CountingVisitor.this
def visitKeyValue(s: Any): Unit = {}
def visitValue(v: Any, index: Int): Unit = count += 1
def visitEnd(index: Int) = count
}
override def visitString(s: CharSequence, index: Int) = { count += 1; count }
override def visitNum(d: Double, decIndex: Int, expIndex: Int, index: Int) = { count += 1; count }
override def visitBool(b: Boolean, index: Int) = { count += 1; count }
override def visitNull(index: Int) = { count += 1; count }
}
// Count elements in JSON
val jsonString = """{"users":[{"name":"Alice"},{"name":"Bob"}],"count":2}"""
val elementCount = ujson.transform(jsonString, new CountingVisitor())
println(s"JSON contains $elementCount elements")
// Deep copy ujson.Value
val original = ujson.Obj("data" -> ujson.Arr(ujson.Num(1), ujson.Num(2)))
val copied = ujson.copy(original)
// Modify copy without affecting original
copied("data").arr += ujson.Num(3)
println(ujson.write(original)) // {"data":[1,2]}
println(ujson.write(copied)) // {"data":[1,2,3]}Using ujson integration for heterogeneous data structures.
Usage Examples:
import upickle.default._
import ujson._
// Working with mixed data types
case class MixedData(
metadata: ujson.Obj,
payload: ujson.Value,
timestamp: Long
)
implicit val mixedDataRW: ReadWriter[MixedData] = macroRW
val mixedData = MixedData(
metadata = ujson.Obj("version" -> ujson.Str("1.0"), "source" -> ujson.Str("api")),
payload = ujson.Arr(ujson.Num(1), ujson.Str("test"), ujson.Bool(true)),
timestamp = System.currentTimeMillis()
)
val json = write(mixedData)
val parsed = read[MixedData](json)
// Dynamic JSON construction
def buildDynamicJson(data: Map[String, Any]): ujson.Value = {
val obj = ujson.Obj()
data.foreach {
case (key, value: String) => obj(key) = ujson.Str(value)
case (key, value: Int) => obj(key) = ujson.Num(value)
case (key, value: Double) => obj(key) = ujson.Num(value)
case (key, value: Boolean) => obj(key) = ujson.Bool(value)
case (key, value: List[_]) => obj(key) = ujson.Arr(value.map(writeJs): _*)
case (key, null) => obj(key) = ujson.Null
case (key, value) => obj(key) = writeJs(value)
}
obj
}
val dynamicData = Map(
"name" -> "Alice",
"age" -> 30,
"active" -> true,
"scores" -> List(85.5, 90.0, 88.5)
)
val dynamicJson = buildDynamicJson(dynamicData)
val jsonString = write(dynamicJson)/**
* ujson.Value is the base type for all JSON AST nodes
*/
sealed trait ujson.Value {
def apply(s: ujson.Selector): ujson.Value
def update(s: ujson.Selector, v: ujson.Value): Unit
}
/**
* Specific ujson value types
*/
case class ujson.Obj(value: mutable.LinkedHashMap[String, ujson.Value]) extends ujson.Value
case class ujson.Arr(value: mutable.Buffer[ujson.Value]) extends ujson.Value
case class ujson.Str(value: String) extends ujson.Value
case class ujson.Num(value: Double) extends ujson.Value
case class ujson.Bool(value: Boolean) extends ujson.Value
case object ujson.True extends ujson.Bool(true)
case object ujson.False extends ujson.Bool(false)
case object ujson.Null extends ujson.Value
/**
* ujson.Readable represents sources that can provide JSON data
*/
trait ujson.Readable {
def transform[T](f: ujson.Visitor[_, T]): T
}