0
# Type Classes
1
2
Reader, Writer, and ReadWriter type classes define serialization behavior for types in uPickle. These are the core abstractions that enable automatic and custom serialization.
3
4
## Capabilities
5
6
### Reader Type Class
7
8
Represents the ability to read (deserialize) a value of type T from structured data.
9
10
```scala { .api }
11
/**
12
* Represents the ability to read a value of type T
13
* Extends Visitor for processing structured data traversal
14
*/
15
trait Reader[T] extends upickle.core.Visitor[Any, T] {
16
def map[Z](f: T => Z): Reader[Z]
17
def mapNulls[Z](f: T => Z): Reader[Z]
18
def narrow[K <: T]: Reader[K]
19
}
20
```
21
22
**Usage Examples:**
23
24
```scala
25
import upickle.default._
26
import upickle.core._
27
28
// Custom reader for a simple case
29
implicit val customIntReader: Reader[Int] = new SimpleReader[Int] {
30
override def expectedMsg = "expected integer"
31
override def visitInt32(d: Int, index: Int) = d
32
override def visitString(s: CharSequence, index: Int) = s.toString.toInt
33
}
34
35
// Transform existing reader
36
val positiveIntReader = IntReader.map(math.abs)
37
38
// Use reader directly
39
val result = customIntReader.transform(ujson.parse("42"))
40
```
41
42
### Writer Type Class
43
44
Represents the ability to write (serialize) a value of type T to structured data.
45
46
```scala { .api }
47
/**
48
* Represents the ability to write a value of type T
49
* Extends Transformer for converting values to structured data
50
*/
51
trait Writer[T] extends Transformer[T] {
52
def isJsonDictKey: Boolean
53
def narrow[K]: Writer[K]
54
def write0[V](out: Visitor[_, V], v: T): V
55
def write[V](out: Visitor[_, V], v: T): V
56
def comap[U](f: U => T): Writer[U]
57
def comapNulls[U](f: U => T): Writer[U]
58
}
59
```
60
61
**Usage Examples:**
62
63
```scala
64
import upickle.default._
65
import upickle.core._
66
67
// Custom writer for a simple case
68
implicit val customIntWriter: Writer[Int] = new Writer[Int] {
69
override def isJsonDictKey = true
70
def write0[V](out: Visitor[_, V], v: Int): V = {
71
out.visitString(s"number_$v", -1)
72
}
73
}
74
75
// Transform existing writer
76
case class UserId(id: Int)
77
implicit val userIdWriter: Writer[UserId] = IntWriter.comap(_.id)
78
79
// Use writer directly
80
val json = customIntWriter.transform(42, ujson.StringRenderer()).toString
81
```
82
83
### ReadWriter Type Class
84
85
Combined Reader and Writer with additional utilities for bidirectional serialization.
86
87
```scala { .api }
88
/**
89
* A combined Reader and Writer, along with some utility methods
90
*/
91
trait ReadWriter[T] extends Reader[T] with Writer[T] {
92
override def narrow[K]: ReadWriter[K]
93
def bimap[V](f: V => T, g: T => V): ReadWriter[V]
94
}
95
```
96
97
**Usage Examples:**
98
99
```scala
100
import upickle.default._
101
102
// Custom ReadWriter
103
case class Temperature(celsius: Double)
104
105
implicit val temperatureRW: ReadWriter[Temperature] =
106
readwriter[Double].bimap(Temperature(_), _.celsius)
107
108
// Use bidirectionally
109
val temp = Temperature(25.0)
110
val json = write(temp) // Uses Writer part
111
val parsed = read[Temperature](json) // Uses Reader part
112
```
113
114
### Reader Companion Object
115
116
Utilities for creating and combining Reader instances.
117
118
```scala { .api }
119
object Reader {
120
/**
121
* Merges multiple tagged readers for sealed trait handling
122
*/
123
def merge[T](tagKey: String, readers: Reader[_ <: T]*): TaggedReader.Node[T]
124
def merge[T](readers: Reader[_ <: T]*): TaggedReader.Node[T]
125
126
/**
127
* Delegates reader functionality to another visitor
128
*/
129
class Delegate[T, V](delegatedReader: Visitor[T, V]) extends Reader[V]
130
131
/**
132
* Maps reader output through a transformation function
133
*/
134
abstract class MapReader[-T, V, Z](delegatedReader: Visitor[T, V]) extends Reader[Z]
135
}
136
```
137
138
**Usage Examples:**
139
140
```scala
141
import upickle.default._
142
import upickle.core._
143
144
// Merge readers for sealed trait
145
sealed trait Animal
146
case class Dog(name: String) extends Animal
147
case class Cat(name: String) extends Animal
148
149
val animalReader = Reader.merge[Animal](
150
reader[Dog], reader[Cat]
151
)
152
```
153
154
### Writer Companion Object
155
156
Utilities for creating and combining Writer instances.
157
158
```scala { .api }
159
object Writer {
160
/**
161
* Merges multiple tagged writers for sealed trait handling
162
*/
163
def merge[T](writers: Writer[_ <: T]*): TaggedWriter.Node[T]
164
165
/**
166
* Maps writer input through a transformation function
167
*/
168
class MapWriter[U, T](src: Writer[T], f: U => T) extends Writer[U]
169
170
/**
171
* Maps writer input with null handling
172
*/
173
class MapWriterNulls[U, T](src: Writer[T], f: U => T) extends Writer[U]
174
}
175
```
176
177
### ReadWriter Companion Object
178
179
Utilities for creating and combining ReadWriter instances.
180
181
```scala { .api }
182
object ReadWriter {
183
/**
184
* Merges multiple tagged readwriters for sealed trait handling
185
*/
186
def merge[T](tagKey: String, rws: ReadWriter[_ <: T]*): TaggedReadWriter[T]
187
def merge[T](rws: ReadWriter[_ <: T]*): TaggedReadWriter[T]
188
189
/**
190
* Joins separate Reader and Writer into ReadWriter
191
*/
192
implicit def join[T](implicit r0: Reader[T], w0: Writer[T]): ReadWriter[T]
193
194
/**
195
* Delegates readwriter functionality
196
*/
197
abstract class Delegate[T](other: Visitor[Any, T]) extends ReadWriter[T]
198
}
199
```
200
201
**Usage Examples:**
202
203
```scala
204
import upickle.default._
205
206
// Join separate instances
207
implicit val stringReader: Reader[String] = StringReader
208
implicit val stringWriter: Writer[String] = StringWriter
209
implicit val stringRW: ReadWriter[String] = ReadWriter.join[String]
210
211
// Merge for sealed traits
212
sealed trait Shape
213
case class Circle(radius: Double) extends Shape
214
case class Rectangle(width: Double, height: Double) extends Shape
215
216
implicit val shapeRW = ReadWriter.merge[Shape](
217
readwriter[Circle], readwriter[Rectangle]
218
)
219
```
220
221
### Key Utilities
222
223
Functions for marking types as suitable for JSON dictionary keys.
224
225
```scala { .api }
226
/**
227
* Mark a ReadWriter[T] as something that can be used as a key in a JSON dictionary
228
*/
229
def stringKeyRW[T](readwriter: ReadWriter[T]): ReadWriter[T]
230
231
/**
232
* Mark a Writer[T] as something that can be used as a key in a JSON dictionary
233
*/
234
def stringKeyW[T](readwriter: Writer[T]): Writer[T]
235
```
236
237
**Usage Examples:**
238
239
```scala
240
import upickle.default._
241
242
// Custom ID type that can be used as map key
243
case class ProductId(id: String)
244
245
implicit val productIdRW: ReadWriter[ProductId] =
246
stringKeyRW(readwriter[String].bimap(ProductId(_), _.id))
247
248
// Now can serialize Map[ProductId, Product]
249
val productMap: Map[ProductId, Product] = Map(
250
ProductId("abc") -> Product("Widget", 10.0)
251
)
252
253
val json = write(productMap)
254
// Result: {"abc": {"name": "Widget", "price": 10.0}}
255
```
256
257
## Types
258
259
```scala { .api }
260
trait SimpleReader[T] extends Reader[T] with upickle.core.SimpleVisitor[Any, T]
261
262
trait ObjectWriter[T] extends Writer[T] {
263
def length(v: T): Int
264
def writeToObject[R](ctx: ObjVisitor[_, R], v: T): Unit
265
}
266
267
/**
268
* Implicit to indicate that we are currently deriving an implicit T
269
* Used to avoid infinite recursion in implicit derivation
270
*/
271
class CurrentlyDeriving[T]
272
```