0
# Custom Serializers
1
2
Apache Fury's extensible serializer framework allows for custom serialization logic, specialized type handling, and performance optimizations for specific use cases.
3
4
## Serializer Base Classes
5
6
### Serializer
7
8
Base abstract class for all custom serializers.
9
10
```java { .api }
11
public abstract class Serializer<T> {
12
// Core serialization methods
13
public abstract void write(MemoryBuffer buffer, T value);
14
public abstract T read(MemoryBuffer buffer);
15
16
// Type information
17
public abstract Class<T> getType();
18
public short getTypeId();
19
20
// Reference tracking
21
public boolean needToWriteRef();
22
public void setNeedToWriteRef(boolean needToWriteRef);
23
24
// Cross-language support
25
public boolean supportCodegenHook();
26
public String codecgen(CodegenContext context);
27
28
// Lifecycle methods
29
public void setFury(Fury fury);
30
public Fury getFury();
31
}
32
```
33
34
### AbstractObjectSerializer
35
36
Base class for object serializers with field-based serialization.
37
38
```java { .api }
39
public abstract class AbstractObjectSerializer<T> extends Serializer<T> {
40
// Field-based serialization support
41
protected void writeFields(MemoryBuffer buffer, T obj);
42
protected T readFields(MemoryBuffer buffer, T obj);
43
44
// Object creation
45
protected T newInstance();
46
protected T newInstance(Class<?> clazz);
47
48
// Field access
49
protected Object getFieldValue(Object obj, String fieldName);
50
protected void setFieldValue(Object obj, String fieldName, Object value);
51
}
52
```
53
54
## Specialized Serializers
55
56
### CodegenSerializer
57
58
Base class for code-generated serializers providing optimal performance.
59
60
```java { .api }
61
public abstract class CodegenSerializer<T> extends Serializer<T> {
62
// Generated serialization methods
63
@Override
64
public void write(MemoryBuffer buffer, T value);
65
@Override
66
public T read(MemoryBuffer buffer);
67
68
// Code generation support
69
public static String genCode(Class<?> clazz, CodegenContext context);
70
}
71
```
72
73
### CompatibleSerializer
74
75
Serializer supporting schema evolution and backward compatibility.
76
77
```java { .api }
78
public abstract class CompatibleSerializer<T> extends AbstractObjectSerializer<T> {
79
// Schema compatibility support
80
protected void writeTypeAndObject(MemoryBuffer buffer, T obj);
81
protected T readTypeAndObject(MemoryBuffer buffer);
82
83
// Schema evolution
84
protected boolean isSchemaCompatible(ClassDef classDef);
85
protected void handleSchemaEvolution(MemoryBuffer buffer, T obj, ClassDef classDef);
86
}
87
```
88
89
### ImmutableSerializer
90
91
Optimized serializer for immutable objects.
92
93
```java { .api }
94
public abstract class ImmutableSerializer<T> extends Serializer<T> {
95
// Immutable object handling
96
@Override
97
public final boolean needToWriteRef();
98
99
// Optimized for immutable types
100
protected abstract T createInstance(Object... args);
101
}
102
```
103
104
## Collection Serializers
105
106
### AbstractCollectionSerializer
107
108
Base class for collection serializers.
109
110
```java { .api }
111
public abstract class AbstractCollectionSerializer<T> extends Serializer<T> {
112
// Collection serialization
113
protected void writeElements(MemoryBuffer buffer, Collection<?> collection);
114
protected Collection<Object> readElements(MemoryBuffer buffer, Collection<Object> collection);
115
116
// Collection metadata
117
protected void writeCollectionInfo(MemoryBuffer buffer, Collection<?> collection);
118
protected CollectionInfo readCollectionInfo(MemoryBuffer buffer);
119
120
// Element handling
121
protected void writeElement(MemoryBuffer buffer, Object element);
122
protected Object readElement(MemoryBuffer buffer);
123
}
124
```
125
126
### AbstractMapSerializer
127
128
Base class for map serializers.
129
130
```java { .api }
131
public abstract class AbstractMapSerializer<T> extends Serializer<T> {
132
// Map serialization
133
protected void writeKVs(MemoryBuffer buffer, Map<?, ?> map);
134
protected Map<Object, Object> readKVs(MemoryBuffer buffer, Map<Object, Object> map);
135
136
// Map metadata
137
protected void writeMapInfo(MemoryBuffer buffer, Map<?, ?> map);
138
protected MapInfo readMapInfo(MemoryBuffer buffer);
139
140
// Key-value handling
141
protected void writeKV(MemoryBuffer buffer, Object key, Object value);
142
protected Map.Entry<Object, Object> readKV(MemoryBuffer buffer);
143
}
144
```
145
146
## Serializer Factory
147
148
Factory for creating and managing serializers.
149
150
```java { .api }
151
public class SerializerFactory {
152
// Serializer creation
153
public static <T> Serializer<T> createSerializer(Fury fury, Class<T> type);
154
public static <T> Serializer<T> createObjectSerializer(Fury fury, Class<T> type);
155
public static <T> Serializer<T> createCompatibleSerializer(Fury fury, Class<T> type);
156
157
// Built-in serializers
158
public static Serializer<?> createCollectionSerializer(Fury fury, Class<?> collectionType);
159
public static Serializer<?> createMapSerializer(Fury fury, Class<?> mapType);
160
public static Serializer<?> createArraySerializer(Fury fury, Class<?> arrayType);
161
162
// Serializer utilities
163
public static boolean canCreateSerializer(Class<?> type);
164
public static boolean needsCustomSerializer(Class<?> type);
165
}
166
```
167
168
## Built-in Serializers
169
170
### Primitive Serializers
171
172
```java { .api }
173
public class PrimitiveSerializers {
174
public static class BooleanSerializer extends Serializer<Boolean>;
175
public static class ByteSerializer extends Serializer<Byte>;
176
public static class ShortSerializer extends Serializer<Short>;
177
public static class IntSerializer extends Serializer<Integer>;
178
public static class LongSerializer extends Serializer<Long>;
179
public static class FloatSerializer extends Serializer<Float>;
180
public static class DoubleSerializer extends Serializer<Double>;
181
public static class CharSerializer extends Serializer<Character>;
182
}
183
```
184
185
### String Serializers
186
187
```java { .api }
188
public class StringSerializer extends Serializer<String> {
189
// Optimized string serialization
190
@Override
191
public void write(MemoryBuffer buffer, String value);
192
@Override
193
public String read(MemoryBuffer buffer);
194
195
// String encoding options
196
public void setEncoding(Charset encoding);
197
public void setCompressed(boolean compressed);
198
}
199
```
200
201
### Time Serializers
202
203
```java { .api }
204
public class TimeSerializers {
205
public static class DateSerializer extends Serializer<Date>;
206
public static class TimestampSerializer extends Serializer<Timestamp>;
207
public static class InstantSerializer extends Serializer<Instant>;
208
public static class LocalDateSerializer extends Serializer<LocalDate>;
209
public static class LocalTimeSerializer extends Serializer<LocalTime>;
210
public static class LocalDateTimeSerializer extends Serializer<LocalDateTime>;
211
}
212
```
213
214
## Usage Examples
215
216
### Creating Custom Serializers
217
218
```java
219
import org.apache.fury.Fury;
220
import org.apache.fury.memory.MemoryBuffer;
221
import org.apache.fury.serializer.Serializer;
222
223
// Custom class to serialize
224
public class Point {
225
private double x, y;
226
227
public Point(double x, double y) {
228
this.x = x;
229
this.y = y;
230
}
231
232
// getters and setters...
233
}
234
235
// Custom serializer
236
public class PointSerializer extends Serializer<Point> {
237
@Override
238
public void write(MemoryBuffer buffer, Point point) {
239
buffer.writeDouble(point.getX());
240
buffer.writeDouble(point.getY());
241
}
242
243
@Override
244
public Point read(MemoryBuffer buffer) {
245
double x = buffer.readDouble();
246
double y = buffer.readDouble();
247
return new Point(x, y);
248
}
249
250
@Override
251
public Class<Point> getType() {
252
return Point.class;
253
}
254
}
255
256
// Register and use
257
Fury fury = Fury.builder().build();
258
fury.registerSerializer(Point.class, new PointSerializer());
259
260
// Serialization works with custom serializer
261
Point point = new Point(1.5, 2.5);
262
byte[] bytes = fury.serialize(point);
263
Point restored = (Point) fury.deserialize(bytes);
264
```
265
266
### Complex Object Serializer
267
268
```java
269
import org.apache.fury.serializer.AbstractObjectSerializer;
270
271
public class UserSerializer extends AbstractObjectSerializer<User> {
272
@Override
273
public void write(MemoryBuffer buffer, User user) {
274
// Write fields in specific order
275
buffer.writeString(user.getName());
276
buffer.writeInt(user.getAge());
277
buffer.writeBoolean(user.isActive());
278
279
// Handle optional fields
280
if (user.getEmail() != null) {
281
buffer.writeBoolean(true);
282
buffer.writeString(user.getEmail());
283
} else {
284
buffer.writeBoolean(false);
285
}
286
287
// Serialize nested objects
288
getFury().writeRef(buffer, user.getAddress());
289
}
290
291
@Override
292
public User read(MemoryBuffer buffer) {
293
String name = buffer.readString();
294
int age = buffer.readInt();
295
boolean active = buffer.readBoolean();
296
297
// Handle optional fields
298
String email = null;
299
if (buffer.readBoolean()) {
300
email = buffer.readString();
301
}
302
303
// Deserialize nested objects
304
Address address = (Address) getFury().readRef(buffer);
305
306
return new User(name, age, active, email, address);
307
}
308
309
@Override
310
public Class<User> getType() {
311
return User.class;
312
}
313
}
314
```
315
316
### Collection Serializer
317
318
```java
319
import org.apache.fury.serializer.AbstractCollectionSerializer;
320
321
public class CustomListSerializer extends AbstractCollectionSerializer<CustomList> {
322
@Override
323
public void write(MemoryBuffer buffer, CustomList list) {
324
// Write size
325
buffer.writeInt(list.size());
326
327
// Write custom metadata
328
buffer.writeString(list.getName());
329
buffer.writeBoolean(list.isSorted());
330
331
// Write elements
332
for (Object element : list) {
333
getFury().writeRef(buffer, element);
334
}
335
}
336
337
@Override
338
public CustomList read(MemoryBuffer buffer) {
339
int size = buffer.readInt();
340
String name = buffer.readString();
341
boolean sorted = buffer.readBoolean();
342
343
CustomList list = new CustomList(name, sorted);
344
345
// Read elements
346
for (int i = 0; i < size; i++) {
347
Object element = getFury().readRef(buffer);
348
list.add(element);
349
}
350
351
return list;
352
}
353
354
@Override
355
public Class<CustomList> getType() {
356
return CustomList.class;
357
}
358
}
359
```
360
361
### Immutable Object Serializer
362
363
```java
364
import org.apache.fury.serializer.ImmutableSerializer;
365
366
public class ImmutablePersonSerializer extends ImmutableSerializer<ImmutablePerson> {
367
@Override
368
public void write(MemoryBuffer buffer, ImmutablePerson person) {
369
buffer.writeString(person.getName());
370
buffer.writeInt(person.getAge());
371
buffer.writeString(person.getEmail());
372
}
373
374
@Override
375
public ImmutablePerson read(MemoryBuffer buffer) {
376
String name = buffer.readString();
377
int age = buffer.readInt();
378
String email = buffer.readString();
379
380
return ImmutablePerson.builder()
381
.name(name)
382
.age(age)
383
.email(email)
384
.build();
385
}
386
387
@Override
388
protected ImmutablePerson createInstance(Object... args) {
389
return ImmutablePerson.builder()
390
.name((String) args[0])
391
.age((Integer) args[1])
392
.email((String) args[2])
393
.build();
394
}
395
396
@Override
397
public Class<ImmutablePerson> getType() {
398
return ImmutablePerson.class;
399
}
400
}
401
```
402
403
### Enum Serializer
404
405
```java
406
public class CustomEnumSerializer extends Serializer<MyEnum> {
407
@Override
408
public void write(MemoryBuffer buffer, MyEnum value) {
409
// Write enum ordinal for efficiency
410
buffer.writeInt(value.ordinal());
411
}
412
413
@Override
414
public MyEnum read(MemoryBuffer buffer) {
415
int ordinal = buffer.readInt();
416
return MyEnum.values()[ordinal];
417
}
418
419
@Override
420
public Class<MyEnum> getType() {
421
return MyEnum.class;
422
}
423
424
// Disable reference tracking for enums
425
@Override
426
public boolean needToWriteRef() {
427
return false;
428
}
429
}
430
```
431
432
### Versioned Serializer
433
434
```java
435
import org.apache.fury.serializer.CompatibleSerializer;
436
437
public class VersionedUserSerializer extends CompatibleSerializer<User> {
438
private static final int VERSION = 2;
439
440
@Override
441
public void write(MemoryBuffer buffer, User user) {
442
// Write version first
443
buffer.writeInt(VERSION);
444
445
// Write fields based on current version
446
buffer.writeString(user.getName());
447
buffer.writeInt(user.getAge());
448
449
if (VERSION >= 2) {
450
buffer.writeString(user.getEmail()); // Added in version 2
451
}
452
}
453
454
@Override
455
public User read(MemoryBuffer buffer) {
456
int version = buffer.readInt();
457
458
String name = buffer.readString();
459
int age = buffer.readInt();
460
461
String email = null;
462
if (version >= 2) {
463
email = buffer.readString();
464
}
465
466
return new User(name, age, email);
467
}
468
469
@Override
470
public Class<User> getType() {
471
return User.class;
472
}
473
}
474
```
475
476
### Serializer Registration
477
478
```java
479
public class SerializerRegistration {
480
public static void registerCustomSerializers(Fury fury) {
481
// Register individual serializers
482
fury.registerSerializer(Point.class, new PointSerializer());
483
fury.registerSerializer(User.class, new UserSerializer());
484
485
// Register serializer class (instantiated by Fury)
486
fury.registerSerializer(CustomList.class, CustomListSerializer.class);
487
488
// Register with custom factory
489
fury.registerSerializer(ImmutablePerson.class,
490
(f) -> new ImmutablePersonSerializer());
491
}
492
}
493
```
494
495
## Advanced Patterns
496
497
### Conditional Serialization
498
499
```java
500
public class ConditionalSerializer extends Serializer<ConditionalObject> {
501
@Override
502
public void write(MemoryBuffer buffer, ConditionalObject obj) {
503
// Write discriminant
504
buffer.writeByte(obj.getType().ordinal());
505
506
// Conditional serialization based on type
507
switch (obj.getType()) {
508
case SIMPLE:
509
buffer.writeString(obj.getSimpleValue());
510
break;
511
case COMPLEX:
512
getFury().writeRef(buffer, obj.getComplexValue());
513
break;
514
case OPTIMIZED:
515
writeOptimizedFormat(buffer, obj);
516
break;
517
}
518
}
519
520
@Override
521
public ConditionalObject read(MemoryBuffer buffer) {
522
ObjectType type = ObjectType.values()[buffer.readByte()];
523
524
switch (type) {
525
case SIMPLE:
526
return new ConditionalObject(type, buffer.readString());
527
case COMPLEX:
528
return new ConditionalObject(type, getFury().readRef(buffer));
529
case OPTIMIZED:
530
return readOptimizedFormat(buffer);
531
default:
532
throw new IllegalStateException("Unknown type: " + type);
533
}
534
}
535
}
536
```
537
538
### Performance Optimizations
539
540
```java
541
public class OptimizedSerializer extends Serializer<LargeObject> {
542
// Cache field accessors for performance
543
private final FieldAccessor[] fieldAccessors;
544
545
public OptimizedSerializer() {
546
Field[] fields = ReflectionUtils.getFields(LargeObject.class);
547
fieldAccessors = new FieldAccessor[fields.length];
548
for (int i = 0; i < fields.length; i++) {
549
fieldAccessors[i] = FieldAccessor.createUnsafeAccessor(fields[i]);
550
}
551
}
552
553
@Override
554
public void write(MemoryBuffer buffer, LargeObject obj) {
555
// Use cached accessors for maximum performance
556
for (FieldAccessor accessor : fieldAccessors) {
557
Class<?> type = accessor.getType();
558
if (type == int.class) {
559
buffer.writeInt(accessor.getInt(obj));
560
} else if (type == String.class) {
561
buffer.writeString((String) accessor.get(obj));
562
}
563
// ... handle other types
564
}
565
}
566
567
@Override
568
public Class<LargeObject> getType() {
569
return LargeObject.class;
570
}
571
}
572
```
573
574
## Performance Considerations
575
576
### Serializer Design
577
578
- **Minimize Object Creation**: Reuse objects and avoid unnecessary allocations
579
- **Use Primitive Operations**: Access primitive fields directly for better performance
580
- **Cache Metadata**: Cache field accessors and type information
581
- **Optimize Hot Paths**: Focus on frequently serialized types
582
583
### Registration Strategy
584
585
- **Register Early**: Register serializers during application startup
586
- **Use Type IDs**: Assign explicit type IDs for better performance
587
- **Batch Registration**: Register related serializers together
588
- **Monitor Performance**: Profile serialization hotspots
589
590
### Memory Efficiency
591
592
- **Compact Formats**: Use efficient binary representations
593
- **Avoid Boxing**: Work with primitives when possible
594
- **Reference Management**: Carefully manage reference tracking needs
595
- **Buffer Usage**: Minimize buffer allocations and copies