0
# Serialization
1
2
Serialization capabilities for saving and loading scanned metadata in multiple formats including XML, JSON, and Java code generation. This enables persistent storage and sharing of metadata across different application runs and environments.
3
4
## Capabilities
5
6
### Serializer Interface
7
8
Base contract for all serialization implementations providing read and write operations.
9
10
```java { .api }
11
/**
12
* Contract for serializing/deserializing Reflections metadata
13
*/
14
interface Serializer {
15
/** Read Reflections metadata from input stream */
16
Reflections read(InputStream inputStream);
17
18
/** Save Reflections metadata to file */
19
File save(Reflections reflections, String filename);
20
21
/** Prepare file with parent directories (static utility) */
22
static File prepareFile(String filename);
23
}
24
```
25
26
### XML Serialization
27
28
XML format serialization for human-readable and widely compatible metadata storage.
29
30
```java { .api }
31
/**
32
* XML serialization format implementation
33
*/
34
class XmlSerializer implements Serializer {
35
/** Default constructor */
36
XmlSerializer();
37
38
/** Read XML metadata from input stream */
39
Reflections read(InputStream inputStream);
40
41
/** Save metadata as XML file */
42
File save(Reflections reflections, String filename);
43
}
44
```
45
46
### JSON Serialization
47
48
JSON format serialization for lightweight and web-friendly metadata storage.
49
50
```java { .api }
51
/**
52
* JSON serialization format implementation
53
*/
54
class JsonSerializer implements Serializer {
55
/** Default constructor */
56
JsonSerializer();
57
58
/** Read JSON metadata from input stream */
59
Reflections read(InputStream inputStream);
60
61
/** Save metadata as JSON file */
62
File save(Reflections reflections, String filename);
63
}
64
```
65
66
### Java Code Generation
67
68
Java source code generation for type-safe compile-time access to metadata.
69
70
```java { .api }
71
/**
72
* Java source code generation for type-safe access
73
*/
74
class JavaCodeSerializer implements Serializer {
75
/** Default constructor */
76
JavaCodeSerializer();
77
78
/** Not supported - throws UnsupportedOperationException */
79
Reflections read(InputStream inputStream);
80
81
/** Generate Java interface source code with type-safe metadata access methods */
82
File save(Reflections reflections, String name);
83
}
84
```
85
86
### Reflections Serialization Methods
87
88
Built-in serialization methods in the main Reflections class for convenient saving and loading.
89
90
```java { .api }
91
/**
92
* Built-in serialization methods in Reflections class
93
*/
94
class Reflections {
95
/** Save metadata to XML file (default serializer) */
96
File save(String filename);
97
98
/** Save metadata to file using specified serializer */
99
File save(String filename, Serializer serializer);
100
101
/** Collect metadata from input stream using specified serializer */
102
Reflections collect(InputStream inputStream, Serializer serializer);
103
104
/** Collect metadata from file using specified serializer */
105
Reflections collect(File file, Serializer serializer);
106
}
107
```
108
109
### Static Collection Methods
110
111
Static methods for collecting pre-saved metadata from various sources and locations.
112
113
```java { .api }
114
/**
115
* Static methods for collecting saved metadata
116
*/
117
class Reflections {
118
/** Collect saved XML metadata from META-INF/reflections/ with default filter */
119
static Reflections collect();
120
121
/** Collect saved metadata with custom package prefix and resource name filter */
122
static Reflections collect(String packagePrefix, Predicate<String> resourceNameFilter);
123
124
/** Collect saved metadata with custom serializer */
125
static Reflections collect(String packagePrefix, Predicate<String> resourceNameFilter, Serializer serializer);
126
}
127
```
128
129
## Usage Examples
130
131
### Basic XML Serialization
132
133
```java
134
import org.reflections.Reflections;
135
import org.reflections.serializers.XmlSerializer;
136
137
// Create and configure Reflections
138
Reflections reflections = new Reflections("com.mycompany");
139
140
// Save to XML using default serializer
141
File xmlFile = reflections.save("target/reflections-metadata.xml");
142
143
// Save to XML using explicit serializer
144
File explicitXmlFile = reflections.save("target/metadata.xml", new XmlSerializer());
145
146
// Load from XML
147
Reflections loadedReflections = new XmlSerializer().read(new FileInputStream(xmlFile));
148
149
// Collect from file
150
Reflections collected = new Reflections().collect(xmlFile, new XmlSerializer());
151
```
152
153
### JSON Serialization
154
155
```java
156
import org.reflections.serializers.JsonSerializer;
157
import java.io.FileInputStream;
158
159
// Save to JSON
160
JsonSerializer jsonSerializer = new JsonSerializer();
161
File jsonFile = reflections.save("target/reflections-metadata.json", jsonSerializer);
162
163
// Load from JSON
164
Reflections fromJson = jsonSerializer.read(new FileInputStream(jsonFile));
165
166
// Merge JSON data with existing Reflections
167
Reflections existing = new Reflections("com.mycompany.core");
168
Reflections merged = existing.collect(jsonFile, jsonSerializer);
169
```
170
171
### Java Code Generation
172
173
```java
174
import org.reflections.serializers.JavaCodeSerializer;
175
176
// Generate Java interface with type-safe access methods
177
JavaCodeSerializer codeSerializer = new JavaCodeSerializer();
178
File javaFile = reflections.save("src/generated/java/ReflectionsMetadata", codeSerializer);
179
180
// This generates a Java interface like:
181
// public interface ReflectionsMetadata {
182
// Set<String> getSubTypesOf_com_mycompany_Service();
183
// Set<String> getTypesAnnotatedWith_org_springframework_stereotype_Component();
184
// // ... other generated methods
185
// }
186
187
// Note: JavaCodeSerializer.read() is not supported and throws UnsupportedOperationException
188
```
189
190
### Collecting Pre-saved Metadata
191
192
```java
193
import org.reflections.util.FilterBuilder;
194
195
// Collect all saved metadata from META-INF/reflections/
196
Reflections collected = Reflections.collect();
197
198
// Collect with package prefix filter
199
Reflections packageFiltered = Reflections.collect("com.mycompany",
200
resourceName -> resourceName.contains("reflections"));
201
202
// Collect with custom resource filter
203
Reflections customFiltered = Reflections.collect("",
204
new FilterBuilder().includePattern(".*-reflections\\.xml")::test);
205
206
// Collect using different serializer
207
Reflections jsonCollected = Reflections.collect("com.mycompany",
208
resourceName -> resourceName.endsWith(".json"),
209
new JsonSerializer());
210
```
211
212
### Build-time Metadata Generation
213
214
```java
215
// Example build-time metadata generation and collection pattern
216
public class MetadataGenerator {
217
public static void main(String[] args) {
218
// Generate metadata during build
219
Reflections buildTimeReflections = new Reflections(new ConfigurationBuilder()
220
.forPackages("com.mycompany")
221
.setScanners(Scanners.values())
222
.filterInputsBy(new FilterBuilder()
223
.includePackage("com.mycompany")
224
.excludePackage("com.mycompany.test")));
225
226
// Save to build output directory
227
buildTimeReflections.save("target/classes/META-INF/reflections/myapp-reflections.xml");
228
229
// Also generate JSON version
230
buildTimeReflections.save("target/classes/META-INF/reflections/myapp-reflections.json",
231
new JsonSerializer());
232
233
// Generate type-safe access code
234
buildTimeReflections.save("src/generated/java/com/mycompany/ReflectionsMetadata",
235
new JavaCodeSerializer());
236
}
237
}
238
239
// Runtime collection (much faster than scanning)
240
public class RuntimeUsage {
241
public void usePreGeneratedMetadata() {
242
// Collect pre-generated metadata at runtime
243
Reflections runtime = Reflections.collect("META-INF/reflections/",
244
name -> name.contains("myapp-reflections"));
245
246
// Use normally - no scanning overhead
247
Set<Class<?>> services = runtime.get(SubTypes.of(Service.class).asClass());
248
}
249
}
250
```
251
252
### Serialization Configuration and Customization
253
254
```java
255
// Custom serialization workflow
256
public class CustomSerializationWorkflow {
257
258
public void saveMultipleFormats(Reflections reflections, String baseName) {
259
// Save in multiple formats
260
reflections.save(baseName + ".xml", new XmlSerializer());
261
reflections.save(baseName + ".json", new JsonSerializer());
262
263
try {
264
reflections.save(baseName + ".java", new JavaCodeSerializer());
265
} catch (UnsupportedOperationException e) {
266
// Handle JavaCodeSerializer read limitation
267
System.out.println("Java code generation completed, read not supported");
268
}
269
}
270
271
public Reflections loadFromMultipleSources() {
272
Reflections combined = new Reflections();
273
274
// Load and merge from different sources
275
try {
276
combined.merge(new XmlSerializer().read(
277
new FileInputStream("metadata1.xml")));
278
} catch (FileNotFoundException e) {
279
// Handle missing file
280
}
281
282
try {
283
combined.merge(new JsonSerializer().read(
284
new FileInputStream("metadata2.json")));
285
} catch (FileNotFoundException e) {
286
// Handle missing file
287
}
288
289
return combined;
290
}
291
}
292
```
293
294
### Performance Optimization with Serialization
295
296
```java
297
// Optimized metadata loading pattern
298
public class OptimizedMetadataLoading {
299
private static final String METADATA_LOCATION = "META-INF/reflections/";
300
private static Reflections cachedReflections;
301
302
public static synchronized Reflections getReflections() {
303
if (cachedReflections == null) {
304
// Try to load from pre-saved metadata first (fast)
305
try {
306
cachedReflections = Reflections.collect(METADATA_LOCATION,
307
name -> name.endsWith("-reflections.xml"));
308
309
if (!cachedReflections.getStore().isEmpty()) {
310
return cachedReflections;
311
}
312
} catch (Exception e) {
313
// Fall back to scanning if collection fails
314
}
315
316
// Fall back to runtime scanning (slower)
317
cachedReflections = new Reflections(new ConfigurationBuilder()
318
.forPackages("com.mycompany")
319
.setScanners(Scanners.SubTypes, Scanners.TypesAnnotated));
320
321
// Save for next time
322
try {
323
cachedReflections.save("target/generated-reflections.xml");
324
} catch (Exception e) {
325
// Ignore save errors
326
}
327
}
328
329
return cachedReflections;
330
}
331
}
332
```
333
334
### Serialization in Testing
335
336
```java
337
// Testing with serialized metadata
338
public class SerializationTest {
339
340
@Test
341
public void testMetadataSerialization() throws IOException {
342
// Create test metadata
343
Reflections original = new Reflections("com.mycompany.test");
344
345
// Save to temporary file
346
File tempFile = File.createTempFile("test-reflections", ".xml");
347
original.save(tempFile.getAbsolutePath());
348
349
// Load and compare
350
Reflections loaded = new XmlSerializer().read(new FileInputStream(tempFile));
351
352
// Verify metadata integrity
353
assertEquals(original.getStore().size(), loaded.getStore().size());
354
assertEquals(original.getSubTypesOf(TestInterface.class),
355
loaded.getSubTypesOf(TestInterface.class));
356
357
// Cleanup
358
tempFile.delete();
359
}
360
361
@Test
362
public void testJsonSerialization() throws IOException {
363
Reflections original = new Reflections("com.mycompany.test");
364
365
// Test JSON serialization round-trip
366
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
367
new JsonSerializer().save(original, outputStream.toString());
368
369
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
370
Reflections loaded = new JsonSerializer().read(inputStream);
371
372
// Verify equality
373
assertEquals(original.getStore(), loaded.getStore());
374
}
375
}
376
```
377
378
### Error Handling in Serialization
379
380
```java
381
// Robust serialization with error handling
382
public class RobustSerialization {
383
384
public boolean saveWithRetry(Reflections reflections, String filename, int maxRetries) {
385
for (int i = 0; i < maxRetries; i++) {
386
try {
387
reflections.save(filename);
388
return true;
389
} catch (Exception e) {
390
System.err.println("Save attempt " + (i + 1) + " failed: " + e.getMessage());
391
if (i == maxRetries - 1) {
392
e.printStackTrace();
393
}
394
}
395
}
396
return false;
397
}
398
399
public Reflections loadWithFallback(String primaryFile, String fallbackFile) {
400
// Try primary file first
401
try {
402
return new XmlSerializer().read(new FileInputStream(primaryFile));
403
} catch (Exception e) {
404
System.err.println("Failed to load primary metadata: " + e.getMessage());
405
}
406
407
// Try fallback file
408
try {
409
return new XmlSerializer().read(new FileInputStream(fallbackFile));
410
} catch (Exception e) {
411
System.err.println("Failed to load fallback metadata: " + e.getMessage());
412
}
413
414
// Fall back to runtime scanning
415
System.err.println("Falling back to runtime scanning");
416
return new Reflections("com.mycompany");
417
}
418
419
public void validateSerializedMetadata(Reflections reflections) throws IllegalStateException {
420
Store store = reflections.getStore();
421
422
if (store.isEmpty()) {
423
throw new IllegalStateException("Loaded metadata is empty");
424
}
425
426
// Validate expected scanners are present
427
if (!store.containsKey("SubTypes")) {
428
throw new IllegalStateException("SubTypes scanner data missing");
429
}
430
431
if (!store.containsKey("TypesAnnotated")) {
432
throw new IllegalStateException("TypesAnnotated scanner data missing");
433
}
434
435
// Validate data integrity
436
Map<String, Set<String>> subTypes = store.get("SubTypes");
437
if (subTypes.isEmpty()) {
438
System.err.println("Warning: No SubTypes data found");
439
}
440
}
441
}
442
```
443
444
## Serialization Best Practices
445
446
### Format Selection
447
- **XML**: Best for human readability, debugging, and integration with build tools
448
- **JSON**: Best for web applications, RESTful APIs, and lightweight storage
449
- **Java Code**: Best for compile-time type safety and eliminating runtime serialization overhead
450
451
### Performance Considerations
452
- Use serialization for build-time metadata generation to avoid runtime scanning overhead
453
- Cache serialized metadata and check timestamps for invalidation
454
- Use compressed formats for large metadata sets
455
- Consider splitting large metadata into multiple files by package or functionality
456
457
### Build Integration
458
- Generate metadata during build process and include in application artifacts
459
- Use Maven/Gradle plugins to automate metadata generation
460
- Store metadata in classpath locations for automatic discovery
461
- Version metadata files alongside application versions
462
463
### Error Recovery
464
- Always provide fallback to runtime scanning if serialized metadata is corrupted or missing
465
- Validate serialized metadata integrity before use
466
- Use multiple serialization formats for redundancy in critical applications
467
- Implement retry mechanisms for network-based metadata loading
468
469
## Types
470
471
```java { .api }
472
/**
473
* Exception thrown during serialization operations
474
*/
475
class ReflectionsException extends RuntimeException {
476
ReflectionsException(String message);
477
ReflectionsException(String message, Throwable cause);
478
ReflectionsException(Throwable cause);
479
}
480
481
/**
482
* File utility for serialization operations
483
*/
484
interface Serializer {
485
/** Static utility to prepare file with parent directories */
486
static File prepareFile(String filename) {
487
File file = new File(filename);
488
File parent = file.getAbsoluteFile().getParentFile();
489
if (parent != null && !parent.exists()) {
490
parent.mkdirs();
491
}
492
return file;
493
}
494
}
495
```