Reader, Writer, and ReadWriter type classes define serialization behavior for types in uPickle. These are the core abstractions that enable automatic and custom serialization.
Represents the ability to read (deserialize) a value of type T from structured data.
/**
* Represents the ability to read a value of type T
* Extends Visitor for processing structured data traversal
*/
trait Reader[T] extends upickle.core.Visitor[Any, T] {
def map[Z](f: T => Z): Reader[Z]
def mapNulls[Z](f: T => Z): Reader[Z]
def narrow[K <: T]: Reader[K]
}Usage Examples:
import upickle.default._
import upickle.core._
// Custom reader for a simple case
implicit val customIntReader: Reader[Int] = new SimpleReader[Int] {
override def expectedMsg = "expected integer"
override def visitInt32(d: Int, index: Int) = d
override def visitString(s: CharSequence, index: Int) = s.toString.toInt
}
// Transform existing reader
val positiveIntReader = IntReader.map(math.abs)
// Use reader directly
val result = customIntReader.transform(ujson.parse("42"))Represents the ability to write (serialize) a value of type T to structured data.
/**
* Represents the ability to write a value of type T
* Extends Transformer for converting values to structured data
*/
trait Writer[T] extends Transformer[T] {
def isJsonDictKey: Boolean
def narrow[K]: Writer[K]
def write0[V](out: Visitor[_, V], v: T): V
def write[V](out: Visitor[_, V], v: T): V
def comap[U](f: U => T): Writer[U]
def comapNulls[U](f: U => T): Writer[U]
}Usage Examples:
import upickle.default._
import upickle.core._
// Custom writer for a simple case
implicit val customIntWriter: Writer[Int] = new Writer[Int] {
override def isJsonDictKey = true
def write0[V](out: Visitor[_, V], v: Int): V = {
out.visitString(s"number_$v", -1)
}
}
// Transform existing writer
case class UserId(id: Int)
implicit val userIdWriter: Writer[UserId] = IntWriter.comap(_.id)
// Use writer directly
val json = customIntWriter.transform(42, ujson.StringRenderer()).toStringCombined Reader and Writer with additional utilities for bidirectional serialization.
/**
* A combined Reader and Writer, along with some utility methods
*/
trait ReadWriter[T] extends Reader[T] with Writer[T] {
override def narrow[K]: ReadWriter[K]
def bimap[V](f: V => T, g: T => V): ReadWriter[V]
}Usage Examples:
import upickle.default._
// Custom ReadWriter
case class Temperature(celsius: Double)
implicit val temperatureRW: ReadWriter[Temperature] =
readwriter[Double].bimap(Temperature(_), _.celsius)
// Use bidirectionally
val temp = Temperature(25.0)
val json = write(temp) // Uses Writer part
val parsed = read[Temperature](json) // Uses Reader partUtilities for creating and combining Reader instances.
object Reader {
/**
* Merges multiple tagged readers for sealed trait handling
*/
def merge[T](tagKey: String, readers: Reader[_ <: T]*): TaggedReader.Node[T]
def merge[T](readers: Reader[_ <: T]*): TaggedReader.Node[T]
/**
* Delegates reader functionality to another visitor
*/
class Delegate[T, V](delegatedReader: Visitor[T, V]) extends Reader[V]
/**
* Maps reader output through a transformation function
*/
abstract class MapReader[-T, V, Z](delegatedReader: Visitor[T, V]) extends Reader[Z]
}Usage Examples:
import upickle.default._
import upickle.core._
// Merge readers for sealed trait
sealed trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
val animalReader = Reader.merge[Animal](
reader[Dog], reader[Cat]
)Utilities for creating and combining Writer instances.
object Writer {
/**
* Merges multiple tagged writers for sealed trait handling
*/
def merge[T](writers: Writer[_ <: T]*): TaggedWriter.Node[T]
/**
* Maps writer input through a transformation function
*/
class MapWriter[U, T](src: Writer[T], f: U => T) extends Writer[U]
/**
* Maps writer input with null handling
*/
class MapWriterNulls[U, T](src: Writer[T], f: U => T) extends Writer[U]
}Utilities for creating and combining ReadWriter instances.
object ReadWriter {
/**
* Merges multiple tagged readwriters for sealed trait handling
*/
def merge[T](tagKey: String, rws: ReadWriter[_ <: T]*): TaggedReadWriter[T]
def merge[T](rws: ReadWriter[_ <: T]*): TaggedReadWriter[T]
/**
* Joins separate Reader and Writer into ReadWriter
*/
implicit def join[T](implicit r0: Reader[T], w0: Writer[T]): ReadWriter[T]
/**
* Delegates readwriter functionality
*/
abstract class Delegate[T](other: Visitor[Any, T]) extends ReadWriter[T]
}Usage Examples:
import upickle.default._
// Join separate instances
implicit val stringReader: Reader[String] = StringReader
implicit val stringWriter: Writer[String] = StringWriter
implicit val stringRW: ReadWriter[String] = ReadWriter.join[String]
// Merge for sealed traits
sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape
implicit val shapeRW = ReadWriter.merge[Shape](
readwriter[Circle], readwriter[Rectangle]
)Functions for marking types as suitable for JSON dictionary keys.
/**
* Mark a ReadWriter[T] as something that can be used as a key in a JSON dictionary
*/
def stringKeyRW[T](readwriter: ReadWriter[T]): ReadWriter[T]
/**
* Mark a Writer[T] as something that can be used as a key in a JSON dictionary
*/
def stringKeyW[T](readwriter: Writer[T]): Writer[T]Usage Examples:
import upickle.default._
// Custom ID type that can be used as map key
case class ProductId(id: String)
implicit val productIdRW: ReadWriter[ProductId] =
stringKeyRW(readwriter[String].bimap(ProductId(_), _.id))
// Now can serialize Map[ProductId, Product]
val productMap: Map[ProductId, Product] = Map(
ProductId("abc") -> Product("Widget", 10.0)
)
val json = write(productMap)
// Result: {"abc": {"name": "Widget", "price": 10.0}}trait SimpleReader[T] extends Reader[T] with upickle.core.SimpleVisitor[Any, T]
trait ObjectWriter[T] extends Writer[T] {
def length(v: T): Int
def writeToObject[R](ctx: ObjVisitor[_, R], v: T): Unit
}
/**
* Implicit to indicate that we are currently deriving an implicit T
* Used to avoid infinite recursion in implicit derivation
*/
class CurrentlyDeriving[T]