0
# Reflection Operations
1
2
Avro's reflection support enables automatic schema generation and data handling for existing Java classes using reflection and annotations. This approach allows seamless integration with existing POJOs and domain objects without requiring code generation.
3
4
## Capabilities
5
6
### Reflect Data Utilities
7
8
Core utilities for reflection-based schema generation and data operations on existing Java classes.
9
10
```java { .api }
11
public class ReflectData extends SpecificData {
12
public static ReflectData get();
13
public static ReflectData get(ClassLoader classLoader);
14
15
// Schema generation from classes
16
public Schema getSchema(Type type);
17
public Schema getSchema(Class<?> type);
18
19
// Allow null values (makes fields nullable by default)
20
public ReflectData allowNull();
21
public boolean getAllowNull();
22
23
// String type configuration
24
public ReflectData setStringType(StringType stringType);
25
26
// Instance creation
27
public Object newInstance(Class<?> c, Schema schema);
28
public Object newRecord(Object old, Schema schema);
29
30
// Field access
31
public Object getField(Object record, String name, int position);
32
public void setField(Object record, String name, int position, Object o);
33
34
// Validation
35
public boolean validate(Schema schema, Object datum);
36
}
37
```
38
39
**Usage Examples:**
40
41
```java
42
// Basic reflection usage
43
ReflectData reflectData = ReflectData.get();
44
45
// Generate schema from existing POJO
46
public class Person {
47
private String name;
48
private int age;
49
private List<String> hobbies;
50
51
// Constructor, getters, setters
52
public Person() {}
53
public Person(String name, int age) {
54
this.name = name;
55
this.age = age;
56
this.hobbies = new ArrayList<>();
57
}
58
59
public String getName() { return name; }
60
public void setName(String name) { this.name = name; }
61
public int getAge() { return age; }
62
public void setAge(int age) { this.age = age; }
63
public List<String> getHobbies() { return hobbies; }
64
public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }
65
}
66
67
// Generate schema automatically
68
Schema personSchema = reflectData.getSchema(Person.class);
69
System.out.println("Generated schema: " + personSchema.toString(true));
70
71
// Allow nullable fields
72
ReflectData nullableReflect = ReflectData.get().allowNull();
73
Schema nullableSchema = nullableReflect.getSchema(Person.class);
74
75
// Create instance from schema
76
Person person = (Person) reflectData.newInstance(Person.class, personSchema);
77
person.setName("Alice");
78
person.setAge(30);
79
80
// Validate instance against schema
81
boolean isValid = reflectData.validate(personSchema, person);
82
System.out.println("Person is valid: " + isValid);
83
```
84
85
### Reflect Datum Reader
86
87
DatumReader that uses reflection to deserialize data into existing Java objects.
88
89
```java { .api }
90
public class ReflectDatumReader<T> extends SpecificDatumReader<T> {
91
public ReflectDatumReader();
92
public ReflectDatumReader(Schema schema);
93
public ReflectDatumReader(Schema writer, Schema reader);
94
public ReflectDatumReader(Class<T> c);
95
public ReflectDatumReader(Class<T> c, Schema writer, Schema reader);
96
97
// Inherited methods
98
public void setSchema(Schema schema);
99
public void setExpected(Schema reader);
100
public T read(T reuse, Decoder in) throws IOException;
101
}
102
```
103
104
**Usage Examples:**
105
106
```java
107
// Read data into existing POJOs
108
ReflectDatumReader<Person> reader = new ReflectDatumReader<>(Person.class);
109
110
// Read from binary data
111
InputStream inputStream = new FileInputStream("persons.avro");
112
BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(inputStream, null);
113
114
Person person = reader.read(null, decoder);
115
System.out.println("Name: " + person.getName());
116
System.out.println("Age: " + person.getAge());
117
118
// Schema evolution with reflection
119
Schema writerSchema = getOldPersonSchema();
120
Schema readerSchema = ReflectData.get().getSchema(Person.class);
121
122
ReflectDatumReader<Person> evolvingReader =
123
new ReflectDatumReader<>(Person.class, writerSchema, readerSchema);
124
125
Person evolvedPerson = evolvingReader.read(null, decoder);
126
127
// Read collection of objects
128
List<Person> persons = new ArrayList<>();
129
ReflectDatumReader<Person> personReader = new ReflectDatumReader<>(Person.class);
130
131
while (hasMoreData(decoder)) {
132
Person p = personReader.read(null, decoder);
133
persons.add(p);
134
}
135
```
136
137
### Reflect Datum Writer
138
139
DatumWriter that uses reflection to serialize existing Java objects.
140
141
```java { .api }
142
public class ReflectDatumWriter<T> extends SpecificDatumWriter<T> {
143
public ReflectDatumWriter();
144
public ReflectDatumWriter(Schema schema);
145
public ReflectDatumWriter(Class<T> c);
146
147
// Inherited methods
148
public void setSchema(Schema schema);
149
public void write(T datum, Encoder out) throws IOException;
150
}
151
```
152
153
**Usage Examples:**
154
155
```java
156
// Write existing POJOs using reflection
157
Person person = new Person("Bob", 35);
158
person.getHobbies().addAll(Arrays.asList("reading", "hiking", "cooking"));
159
160
Schema personSchema = ReflectData.get().getSchema(Person.class);
161
ReflectDatumWriter<Person> writer = new ReflectDatumWriter<>(Person.class);
162
163
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
164
BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(outputStream, null);
165
166
writer.write(person, encoder);
167
encoder.flush();
168
169
// Write collection of objects
170
List<Person> persons = Arrays.asList(
171
new Person("Alice", 25),
172
new Person("Charlie", 40),
173
new Person("Diana", 28)
174
);
175
176
for (Person p : persons) {
177
writer.write(p, encoder);
178
}
179
encoder.flush();
180
181
byte[] serializedData = outputStream.toByteArray();
182
```
183
184
### Reflection Annotations
185
186
Annotations for customizing reflection-based schema generation and field handling.
187
188
```java { .api }
189
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
190
@Retention(RetentionPolicy.RUNTIME)
191
public @interface AvroName {
192
String value();
193
}
194
195
@Target({ElementType.FIELD, ElementType.TYPE})
196
@Retention(RetentionPolicy.RUNTIME)
197
public @interface AvroDoc {
198
String value();
199
}
200
201
@Target(ElementType.FIELD)
202
@Retention(RetentionPolicy.RUNTIME)
203
public @interface AvroDefault {
204
String value();
205
}
206
207
@Target({ElementType.FIELD, ElementType.PARAMETER})
208
@Retention(RetentionPolicy.RUNTIME)
209
public @interface Nullable {
210
// Makes field nullable in generated schema
211
}
212
213
@Target({ElementType.FIELD, ElementType.PARAMETER})
214
@Retention(RetentionPolicy.RUNTIME)
215
public @interface Union {
216
Class<?>[] value();
217
}
218
219
@Target(ElementType.FIELD)
220
@Retention(RetentionPolicy.RUNTIME)
221
public @interface AvroIgnore {
222
// Excludes field from schema generation
223
}
224
225
@Target(ElementType.FIELD)
226
@Retention(RetentionPolicy.RUNTIME)
227
public @interface AvroAlias {
228
String[] value();
229
}
230
231
@Target(ElementType.FIELD)
232
@Retention(RetentionPolicy.RUNTIME)
233
public @interface AvroMeta {
234
String key();
235
String value();
236
}
237
```
238
239
**Usage Examples:**
240
241
```java
242
// POJO with reflection annotations
243
public class AnnotatedPerson {
244
@AvroName("full_name")
245
@AvroDoc("The person's full name")
246
private String name;
247
248
@AvroDoc("Age in years")
249
@AvroDefault("0")
250
private int age;
251
252
@Nullable
253
@AvroDoc("Email address if available")
254
private String email;
255
256
@AvroIgnore
257
private String internalId; // This field will be ignored
258
259
@Union({String.class, Integer.class})
260
private Object flexibleField;
261
262
@AvroAlias({"old_phone", "phone_number"})
263
private String phone;
264
265
@AvroMeta(key = "format", value = "ISO-8601")
266
private String timestamp;
267
268
// Constructors, getters, setters...
269
public AnnotatedPerson() {}
270
271
public String getName() { return name; }
272
public void setName(String name) { this.name = name; }
273
274
public int getAge() { return age; }
275
public void setAge(int age) { this.age = age; }
276
277
@Nullable
278
public String getEmail() { return email; }
279
public void setEmail(String email) { this.email = email; }
280
281
// Other getters/setters...
282
}
283
284
// Generate schema with annotations
285
Schema annotatedSchema = ReflectData.get().getSchema(AnnotatedPerson.class);
286
System.out.println("Schema with annotations:\n" + annotatedSchema.toString(true));
287
288
// The generated schema will include:
289
// - "full_name" instead of "name"
290
// - Documentation strings
291
// - Default values
292
// - Nullable email field
293
// - No internalId field
294
// - Union type for flexibleField
295
// - Aliases for phone field
296
// - Custom metadata
297
```
298
299
### Enum Support
300
301
Reflection support for Java enums with custom naming and documentation.
302
303
```java { .api }
304
// Enum annotations
305
@Target(ElementType.TYPE)
306
@Retention(RetentionPolicy.RUNTIME)
307
public @interface AvroEnumDefault {
308
String value();
309
}
310
```
311
312
**Usage Examples:**
313
314
```java
315
// Enum with reflection support
316
@AvroDoc("Status enumeration for users")
317
public enum UserStatus {
318
@AvroName("ACTIVE") ACTIVE,
319
@AvroName("INACTIVE") INACTIVE,
320
@AvroName("SUSPENDED") SUSPENDED,
321
@AvroName("PENDING") PENDING
322
}
323
324
// POJO using enum
325
public class UserWithStatus {
326
private String name;
327
328
@AvroDefault("\"PENDING\"")
329
private UserStatus status;
330
331
public UserWithStatus() {}
332
333
public String getName() { return name; }
334
public void setName(String name) { this.name = name; }
335
336
public UserStatus getStatus() { return status; }
337
public void setStatus(UserStatus status) { this.status = status; }
338
}
339
340
// Generate schema including enum
341
Schema userWithStatusSchema = ReflectData.get().getSchema(UserWithStatus.class);
342
343
// Use with reflection I/O
344
UserWithStatus user = new UserWithStatus();
345
user.setName("Alice");
346
user.setStatus(UserStatus.ACTIVE);
347
348
ReflectDatumWriter<UserWithStatus> writer = new ReflectDatumWriter<>(UserWithStatus.class);
349
ByteArrayOutputStream out = new ByteArrayOutputStream();
350
BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(out, null);
351
writer.write(user, encoder);
352
encoder.flush();
353
```
354
355
### Complex Type Support
356
357
Reflection handles complex nested types, collections, and custom objects.
358
359
```java { .api }
360
// Complex type examples that work with reflection
361
public class ComplexPerson {
362
private String name;
363
private List<String> hobbies;
364
private Map<String, Integer> scores;
365
private Address address; // Nested object
366
private List<Phone> phoneNumbers; // List of objects
367
368
// Constructors, getters, setters...
369
}
370
371
public class Address {
372
private String street;
373
private String city;
374
private String zipCode;
375
376
@Nullable
377
private String country;
378
379
// Constructors, getters, setters...
380
}
381
382
public class Phone {
383
private String type; // "home", "work", "mobile"
384
private String number;
385
386
// Constructors, getters, setters...
387
}
388
```
389
390
**Usage Examples:**
391
392
```java
393
// Work with complex nested objects
394
ComplexPerson person = new ComplexPerson();
395
person.setName("John Doe");
396
person.setHobbies(Arrays.asList("reading", "swimming"));
397
person.setScores(Map.of("math", 95, "science", 88));
398
399
Address address = new Address();
400
address.setStreet("123 Main St");
401
address.setCity("Anytown");
402
address.setZipCode("12345");
403
person.setAddress(address);
404
405
Phone workPhone = new Phone();
406
workPhone.setType("work");
407
workPhone.setNumber("555-1234");
408
person.setPhoneNumbers(Arrays.asList(workPhone));
409
410
// Generate schema for complex type
411
Schema complexSchema = ReflectData.get().getSchema(ComplexPerson.class);
412
System.out.println("Complex schema:\n" + complexSchema.toString(true));
413
414
// Serialize complex object
415
ReflectDatumWriter<ComplexPerson> writer = new ReflectDatumWriter<>(ComplexPerson.class);
416
ByteArrayOutputStream out = new ByteArrayOutputStream();
417
BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(out, null);
418
writer.write(person, encoder);
419
encoder.flush();
420
421
// Deserialize complex object
422
ReflectDatumReader<ComplexPerson> reader = new ReflectDatumReader<>(ComplexPerson.class);
423
BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(out.toByteArray(), null);
424
ComplexPerson deserializedPerson = reader.read(null, decoder);
425
426
System.out.println("Name: " + deserializedPerson.getName());
427
System.out.println("Address: " + deserializedPerson.getAddress().getCity());
428
```
429
430
## Types
431
432
```java { .api }
433
public class ReflectData extends SpecificData {
434
// Reflection-based data operations
435
}
436
437
public class ReflectDatumReader<T> extends SpecificDatumReader<T> {
438
// Reflection-based reader
439
}
440
441
public class ReflectDatumWriter<T> extends SpecificDatumWriter<T> {
442
// Reflection-based writer
443
}
444
445
// Reflection annotations
446
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
447
@Retention(RetentionPolicy.RUNTIME)
448
public @interface AvroName {
449
String value();
450
}
451
452
@Target({ElementType.FIELD, ElementType.TYPE})
453
@Retention(RetentionPolicy.RUNTIME)
454
public @interface AvroDoc {
455
String value();
456
}
457
458
@Target(ElementType.FIELD)
459
@Retention(RetentionPolicy.RUNTIME)
460
public @interface AvroDefault {
461
String value();
462
}
463
464
@Target({ElementType.FIELD, ElementType.PARAMETER})
465
@Retention(RetentionPolicy.RUNTIME)
466
public @interface Nullable {
467
// Marker annotation for nullable fields
468
}
469
470
@Target({ElementType.FIELD, ElementType.PARAMETER})
471
@Retention(RetentionPolicy.RUNTIME)
472
public @interface Union {
473
Class<?>[] value();
474
}
475
476
@Target(ElementType.FIELD)
477
@Retention(RetentionPolicy.RUNTIME)
478
public @interface AvroIgnore {
479
// Marker annotation to ignore fields
480
}
481
482
@Target(ElementType.FIELD)
483
@Retention(RetentionPolicy.RUNTIME)
484
public @interface AvroAlias {
485
String[] value();
486
}
487
488
@Target(ElementType.FIELD)
489
@Retention(RetentionPolicy.RUNTIME)
490
public @interface AvroMeta {
491
String key();
492
String value();
493
}
494
495
@Target(ElementType.TYPE)
496
@Retention(RetentionPolicy.RUNTIME)
497
public @interface AvroEnumDefault {
498
String value();
499
}
500
```