or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

builtin-types.mdcore-serialization.mdindex.mdjson-integration.mdmessagepack-integration.mdsealed-traits.mdstreaming.mdtype-classes.md
tile.json

json-integration.mddocs/

JSON Integration

Direct integration with ujson for working with JSON AST and value types. This provides seamless interoperability between uPickle serialization and ujson manipulation.

Capabilities

JSON AST Writing

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.Value

Usage 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)

ujson.Value Reader and Writer

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)

Specific ujson Type Readers

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    // Double

Specific ujson Type Writers

Writers 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))      // 42

JSON Transformation Patterns

Common 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]

Direct ujson Operations

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)

JSON Validation

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): Unit

Usage 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
  }
}

JSON Reformatting

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()
}

Low-level JSON Processing

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.Value

Usage 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]}

Mixed Type Serialization

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)

Types

/**
 * 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
}