0
# Modules
1
2
Jackson's module system provides a clean and extensible way to add functionality to ObjectMapper without modifying core classes. Modules can register custom serializers, deserializers, type resolvers, naming strategies, and other components. This system enables both Jackson's own extensions and third-party libraries to integrate seamlessly.
3
4
## Module Base Class
5
6
### Module
7
8
Module is the abstract base class for all Jackson modules.
9
10
```java { .api }
11
public abstract class Module implements Versioned {
12
// Module identification
13
public abstract String getModuleName();
14
public abstract Version version();
15
16
// Module setup - called when module is registered
17
public abstract void setupModule(SetupContext context);
18
19
// Optional setup methods
20
public Object getTypeId();
21
public Iterable<? extends Deserializer> getDeserializers();
22
public Iterable<? extends Serializer> getSerializers();
23
public Iterable<? extends BeanDeserializerModifier> getBeanDeserializerModifiers();
24
public Iterable<? extends BeanSerializerModifier> getBeanSerializerModifiers();
25
26
// Setup context interface
27
public static interface SetupContext {
28
// Version information
29
Version getMapperVersion();
30
31
// Configuration access
32
MapperConfig<?> getConfig();
33
DeserializationConfig getDeserializationConfig();
34
SerializationConfig getSerializationConfig();
35
TypeFactory getTypeFactory();
36
37
// Component registration
38
void addDeserializers(Deserializers d);
39
void addKeyDeserializers(KeyDeserializers d);
40
void addSerializers(Serializers s);
41
void addKeySerializers(Serializers s);
42
void addBeanDeserializerModifier(BeanDeserializerModifier mod);
43
void addBeanSerializerModifier(BeanSerializerModifier mod);
44
void addAbstractTypeResolver(AbstractTypeResolver resolver);
45
void addTypeModifier(TypeModifier modifier);
46
void addValueInstantiators(ValueInstantiators instantiators);
47
48
// Configuration modification
49
void setClassIntrospector(ClassIntrospector ci);
50
void insertAnnotationIntrospector(AnnotationIntrospector ai);
51
void appendAnnotationIntrospector(AnnotationIntrospector ai);
52
void registerSubtypes(Class<?>... subtypes);
53
void registerSubtypes(NamedType... subtypes);
54
void registerSubtypes(Collection<Class<?>> subtypes);
55
void setMixInAnnotations(Class<?> target, Class<?> mixinSource);
56
void addDeserializationProblemHandler(DeserializationProblemHandler handler);
57
void setNamingStrategy(PropertyNamingStrategy naming);
58
59
// Deprecated methods (for compatibility)
60
@Deprecated
61
void addBeanSerializerModifier(BeanSerializerModifier modifier);
62
@Deprecated
63
void addBeanDeserializerModifier(BeanDeserializerModifier modifier);
64
}
65
}
66
```
67
68
## Simple Module Implementation
69
70
### SimpleModule
71
72
SimpleModule provides a convenient base implementation for creating custom modules.
73
74
```java { .api }
75
public class SimpleModule extends Module implements Serializable {
76
// Construction
77
public SimpleModule();
78
public SimpleModule(String name);
79
public SimpleModule(String name, Version version);
80
public SimpleModule(String name, Version version, Map<Class<?>, JsonDeserializer<?>> deserializers);
81
public SimpleModule(String name, Version version, List<JsonSerializer<?>> serializers);
82
public SimpleModule(String name, Version version, Map<Class<?>, JsonDeserializer<?>> deserializers, List<JsonSerializer<?>> serializers);
83
84
// Module identification
85
public String getModuleName();
86
public Version version();
87
public Object getTypeId();
88
89
// Setup
90
public void setupModule(SetupContext context);
91
92
// Serializer registration
93
public SimpleModule addSerializer(JsonSerializer<?> ser);
94
public <T> SimpleModule addSerializer(Class<? extends T> type, JsonSerializer<T> ser);
95
public SimpleModule addKeySerializer(Class<?> type, JsonSerializer<?> ser);
96
97
// Deserializer registration
98
public SimpleModule addDeserializer(Class<?> type, JsonDeserializer<?> deser);
99
public SimpleModule addKeyDeserializer(Class<?> type, KeyDeserializer deser);
100
101
// Abstract type resolution
102
public SimpleModule addAbstractTypeMapping(Class<?> superType, Class<?> subType);
103
public SimpleModule addAbstractTypeResolver(AbstractTypeResolver resolver);
104
105
// Type modification
106
public SimpleModule addTypeModifier(TypeModifier modifier);
107
108
// Value instantiation
109
public SimpleModule addValueInstantiator(Class<?> beanType, ValueInstantiator inst);
110
111
// Mix-in annotations
112
public SimpleModule setMixInAnnotation(Class<?> targetType, Class<?> mixinClass);
113
114
// Naming strategy
115
public SimpleModule setNamingStrategy(PropertyNamingStrategy naming);
116
117
// Modifier registration
118
public SimpleModule setDeserializerModifier(BeanDeserializerModifier mod);
119
public SimpleModule setSerializerModifier(BeanSerializerModifier mod);
120
121
// Subtype registration
122
public SimpleModule registerSubtypes(Class<?>... subtypes);
123
public SimpleModule registerSubtypes(NamedType... subtypes);
124
public SimpleModule registerSubtypes(Collection<Class<?>> subtypes);
125
126
// Introspector modification
127
public SimpleModule setClassIntrospector(ClassIntrospector ci);
128
public SimpleModule insertAnnotationIntrospector(AnnotationIntrospector ai);
129
public SimpleModule appendAnnotationIntrospector(AnnotationIntrospector ai);
130
131
// Problem handler registration
132
public SimpleModule addDeserializationProblemHandler(DeserializationProblemHandler handler);
133
}
134
```
135
136
## Component Interfaces
137
138
### Serializers
139
140
Interface for registering custom serializers.
141
142
```java { .api }
143
public interface Serializers {
144
// Serializer lookup methods
145
JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc);
146
JsonSerializer<?> findReferenceSerializer(SerializationConfig config, ReferenceType type, BeanDescription beanDesc, TypeSerializer contentTypeSerializer, JsonSerializer<Object> contentValueSerializer);
147
JsonSerializer<?> findArraySerializer(SerializationConfig config, ArrayType type, BeanDescription beanDesc, TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
148
JsonSerializer<?> findCollectionSerializer(SerializationConfig config, CollectionType type, BeanDescription beanDesc, TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
149
JsonSerializer<?> findCollectionLikeSerializer(SerializationConfig config, CollectionLikeType type, BeanDescription beanDesc, TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
150
JsonSerializer<?> findMapSerializer(SerializationConfig config, MapType type, BeanDescription beanDesc, JsonSerializer<Object> keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
151
JsonSerializer<?> findMapLikeSerializer(SerializationConfig config, MapLikeType type, BeanDescription beanDesc, JsonSerializer<Object> keySerializer, TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
152
153
// Base implementation
154
public static abstract class Base implements Serializers {
155
// Default implementations returning null
156
}
157
}
158
159
// Utility classes
160
public class SimpleSerializers extends Serializers.Base {
161
// Map-based serializer storage
162
public SimpleSerializers();
163
public SimpleSerializers(Map<Class<?>, JsonSerializer<?>> serializers);
164
165
// Registration methods
166
public SimpleSerializers addSerializer(JsonSerializer<?> ser);
167
public <T> SimpleSerializers addSerializer(Class<? extends T> type, JsonSerializer<T> ser);
168
public void addSerializers(Map<Class<?>, JsonSerializer<?>> serializers);
169
170
// Lookup implementation
171
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc);
172
}
173
```
174
175
### Deserializers
176
177
Interface for registering custom deserializers.
178
179
```java { .api }
180
public interface Deserializers {
181
// Deserializer lookup methods
182
JsonDeserializer<?> findBeanDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException;
183
JsonDeserializer<?> findReferenceDeserializer(ReferenceType refType, DeserializationConfig config, BeanDescription beanDesc, TypeDeserializer contentTypeDeserializer, JsonDeserializer<?> contentDeserializer) throws JsonMappingException;
184
JsonDeserializer<?> findArrayDeserializer(ArrayType type, DeserializationConfig config, BeanDescription beanDesc, TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) throws JsonMappingException;
185
JsonDeserializer<?> findCollectionDeserializer(CollectionType type, DeserializationConfig config, BeanDescription beanDesc, TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) throws JsonMappingException;
186
JsonDeserializer<?> findCollectionLikeDeserializer(CollectionLikeType type, DeserializationConfig config, BeanDescription beanDesc, TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) throws JsonMappingException;
187
JsonDeserializer<?> findMapDeserializer(MapType type, DeserializationConfig config, BeanDescription beanDesc, KeyDeserializer keyDeserializer, TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) throws JsonMappingException;
188
JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type, DeserializationConfig config, BeanDescription beanDesc, KeyDeserializer keyDeserializer, TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) throws JsonMappingException;
189
JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException;
190
JsonDeserializer<?> findTreeNodeDeserializer(Class<? extends JsonNode> nodeType, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException;
191
192
// Base implementation
193
public static abstract class Base implements Deserializers {
194
// Default implementations returning null
195
}
196
}
197
198
// Utility classes
199
public class SimpleDeserializers extends Deserializers.Base {
200
// Map-based deserializer storage
201
public SimpleDeserializers();
202
public SimpleDeserializers(Map<Class<?>, JsonDeserializer<?>> deserializers);
203
204
// Registration methods
205
public SimpleDeserializers addDeserializer(Class<?> type, JsonDeserializer<?> deser);
206
public void addDeserializers(Map<Class<?>, JsonDeserializer<?>> deserializers);
207
208
// Lookup implementation
209
public JsonDeserializer<?> findBeanDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc);
210
public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc);
211
public JsonDeserializer<?> findTreeNodeDeserializer(Class<? extends JsonNode> nodeType, DeserializationConfig config, BeanDescription beanDesc);
212
}
213
```
214
215
### KeyDeserializers
216
217
Interface for registering custom key deserializers for maps.
218
219
```java { .api }
220
public interface KeyDeserializers {
221
// Key deserializer lookup
222
KeyDeserializer findKeyDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException;
223
}
224
225
public class SimpleKeyDeserializers implements KeyDeserializers, Serializable {
226
// Map-based key deserializer storage
227
public SimpleKeyDeserializers();
228
229
// Registration
230
public SimpleKeyDeserializers addDeserializer(Class<?> type, KeyDeserializer deser);
231
232
// Lookup implementation
233
public KeyDeserializer findKeyDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc);
234
}
235
```
236
237
### ValueInstantiators
238
239
Interface for registering custom value instantiators.
240
241
```java { .api }
242
public interface ValueInstantiators {
243
// Value instantiator lookup
244
ValueInstantiator findValueInstantiator(DeserializationConfig config, BeanDescription beanDesc, ValueInstantiator defaultInstantiator);
245
}
246
247
public class SimpleValueInstantiators implements ValueInstantiators, Serializable {
248
// Map-based instantiator storage
249
public SimpleValueInstantiators();
250
251
// Registration
252
public SimpleValueInstantiators addValueInstantiator(Class<?> type, ValueInstantiator inst);
253
254
// Lookup implementation
255
public ValueInstantiator findValueInstantiator(DeserializationConfig config, BeanDescription beanDesc, ValueInstantiator defaultInstantiator);
256
}
257
```
258
259
## Abstract Type Resolution
260
261
### AbstractTypeResolver
262
263
Interface for resolving abstract types to concrete implementations.
264
265
```java { .api }
266
public abstract class AbstractTypeResolver {
267
// Type resolution
268
public abstract JavaType findTypeMapping(DeserializationConfig config, JavaType type) throws JsonMappingException;
269
public abstract JavaType resolveAbstractType(DeserializationConfig config, BeanDescription typeDesc) throws JsonMappingException;
270
}
271
272
public class SimpleAbstractTypeResolver extends AbstractTypeResolver {
273
// Map-based type mapping storage
274
public SimpleAbstractTypeResolver();
275
276
// Mapping registration
277
public <T> SimpleAbstractTypeResolver addMapping(Class<T> superType, Class<? extends T> subType);
278
279
// Resolution implementation
280
public JavaType findTypeMapping(DeserializationConfig config, JavaType type);
281
public JavaType resolveAbstractType(DeserializationConfig config, BeanDescription typeDesc);
282
}
283
```
284
285
## Usage Examples
286
287
### Basic Custom Module
288
289
```java
290
import com.fasterxml.jackson.core.Version;
291
import com.fasterxml.jackson.databind.module.SimpleModule;
292
293
// Create a simple module for custom types
294
public class CustomTypesModule extends SimpleModule {
295
296
public CustomTypesModule() {
297
super("CustomTypesModule", Version.unknownVersion());
298
299
// Add custom serializers
300
addSerializer(Money.class, new MoneySerializer());
301
addSerializer(PhoneNumber.class, new PhoneNumberSerializer());
302
303
// Add custom deserializers
304
addDeserializer(Money.class, new MoneyDeserializer());
305
addDeserializer(PhoneNumber.class, new PhoneNumberDeserializer());
306
307
// Add key deserializers
308
addKeyDeserializer(Money.class, new MoneyKeyDeserializer());
309
310
// Abstract type mappings
311
addAbstractTypeMapping(Payment.class, CreditCardPayment.class);
312
}
313
}
314
315
// Money serializer
316
public class MoneySerializer extends StdSerializer<Money> {
317
public MoneySerializer() {
318
super(Money.class);
319
}
320
321
@Override
322
public void serialize(Money money, JsonGenerator gen, SerializerProvider provider) throws IOException {
323
gen.writeStartObject();
324
gen.writeStringField("currency", money.getCurrency());
325
gen.writeNumberField("amount", money.getAmount());
326
gen.writeEndObject();
327
}
328
}
329
330
// Money deserializer
331
public class MoneyDeserializer extends StdDeserializer<Money> {
332
public MoneyDeserializer() {
333
super(Money.class);
334
}
335
336
@Override
337
public Money deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
338
JsonNode node = p.getCodec().readTree(p);
339
String currency = node.get("currency").asText();
340
BigDecimal amount = new BigDecimal(node.get("amount").asText());
341
return new Money(currency, amount);
342
}
343
}
344
345
// Usage
346
ObjectMapper mapper = new ObjectMapper();
347
mapper.registerModule(new CustomTypesModule());
348
349
Money money = new Money("USD", new BigDecimal("99.99"));
350
String json = mapper.writeValueAsString(money);
351
Money parsed = mapper.readValue(json, Money.class);
352
```
353
354
### Advanced Module with Multiple Components
355
356
```java
357
public class EnhancedValidationModule extends SimpleModule {
358
359
public EnhancedValidationModule() {
360
super("EnhancedValidationModule", new Version(1, 0, 0, null, "com.example", "validation"));
361
362
// Bean deserializer modifier for validation
363
setDeserializerModifier(new ValidationBeanDeserializerModifier());
364
365
// Custom annotation introspector
366
insertAnnotationIntrospector(new ValidationAnnotationIntrospector());
367
368
// Problem handler for validation errors
369
addDeserializationProblemHandler(new ValidationProblemHandler());
370
371
// Custom value instantiator for validated objects
372
addValueInstantiator(ValidatedObject.class, new ValidatedObjectInstantiator());
373
}
374
375
// Custom bean deserializer modifier that adds validation
376
private static class ValidationBeanDeserializerModifier extends BeanDeserializerModifier {
377
378
@Override
379
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
380
// Wrap deserializer with validation if class has validation annotations
381
if (hasValidationAnnotations(beanDesc.getBeanClass())) {
382
return new ValidatingDeserializer(deserializer);
383
}
384
return deserializer;
385
}
386
387
private boolean hasValidationAnnotations(Class<?> beanClass) {
388
// Check for validation annotations
389
return beanClass.isAnnotationPresent(Validated.class) ||
390
Arrays.stream(beanClass.getDeclaredFields())
391
.anyMatch(field -> field.isAnnotationPresent(NotNull.class) ||
392
field.isAnnotationPresent(NotEmpty.class));
393
}
394
}
395
396
// Validating deserializer wrapper
397
private static class ValidatingDeserializer extends DelegatingDeserializer {
398
399
protected ValidatingDeserializer(JsonDeserializer<?> delegatee) {
400
super(delegatee);
401
}
402
403
@Override
404
protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
405
return new ValidatingDeserializer(newDelegatee);
406
}
407
408
@Override
409
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
410
Object result = _delegatee.deserialize(p, ctxt);
411
412
// Perform validation
413
List<String> errors = validateObject(result);
414
if (!errors.isEmpty()) {
415
throw new JsonMappingException(p, "Validation failed: " + String.join(", ", errors));
416
}
417
418
return result;
419
}
420
421
private List<String> validateObject(Object obj) {
422
List<String> errors = new ArrayList<>();
423
Class<?> clazz = obj.getClass();
424
425
// Simple validation logic
426
for (Field field : clazz.getDeclaredFields()) {
427
field.setAccessible(true);
428
429
try {
430
Object value = field.get(obj);
431
432
if (field.isAnnotationPresent(NotNull.class) && value == null) {
433
errors.add(field.getName() + " cannot be null");
434
}
435
436
if (field.isAnnotationPresent(NotEmpty.class)) {
437
if (value == null ||
438
(value instanceof String && ((String) value).isEmpty()) ||
439
(value instanceof Collection && ((Collection<?>) value).isEmpty())) {
440
errors.add(field.getName() + " cannot be empty");
441
}
442
}
443
} catch (IllegalAccessException e) {
444
// Handle reflection errors
445
}
446
}
447
448
return errors;
449
}
450
}
451
}
452
453
// Custom annotations for validation
454
@Retention(RetentionPolicy.RUNTIME)
455
@Target(ElementType.TYPE)
456
public @interface Validated {
457
}
458
459
@Retention(RetentionPolicy.RUNTIME)
460
@Target(ElementType.FIELD)
461
public @interface NotNull {
462
}
463
464
@Retention(RetentionPolicy.RUNTIME)
465
@Target(ElementType.FIELD)
466
public @interface NotEmpty {
467
}
468
469
// Usage
470
@Validated
471
public class User {
472
@NotNull
473
private String username;
474
475
@NotEmpty
476
private String email;
477
478
private String displayName; // Optional
479
480
// constructors, getters, setters
481
}
482
483
ObjectMapper mapper = new ObjectMapper();
484
mapper.registerModule(new EnhancedValidationModule());
485
486
// This will throw validation error for missing required fields
487
String invalidJson = "{\"displayName\":\"John\"}";
488
try {
489
User user = mapper.readValue(invalidJson, User.class);
490
} catch (JsonMappingException e) {
491
System.out.println("Validation failed: " + e.getMessage());
492
}
493
```
494
495
### Conditional Module Registration
496
497
```java
498
public class ConditionalModule extends SimpleModule {
499
500
public ConditionalModule() {
501
super("ConditionalModule");
502
}
503
504
@Override
505
public void setupModule(SetupContext context) {
506
DeserializationConfig deserConfig = context.getDeserializationConfig();
507
SerializationConfig serConfig = context.getSerializationConfig();
508
509
// Only add certain serializers based on configuration
510
if (serConfig.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
511
context.addSerializers(new PrettySerializers());
512
}
513
514
// Add different deserializers based on features
515
if (deserConfig.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
516
context.addDeserializers(new StrictDeserializers());
517
} else {
518
context.addDeserializers(new LenientDeserializers());
519
}
520
521
// Version-based conditional registration
522
Version mapperVersion = context.getMapperVersion();
523
if (mapperVersion.getMajorVersion() >= 2 && mapperVersion.getMinorVersion() >= 15) {
524
context.addTypeModifier(new NewTypeModifier());
525
}
526
}
527
528
private static class PrettySerializers extends Serializers.Base {
529
@Override
530
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {
531
if (type.getRawClass() == ComplexObject.class) {
532
return new PrettyComplexObjectSerializer();
533
}
534
return null;
535
}
536
}
537
}
538
```
539
540
### Module Discovery and Auto-Registration
541
542
```java
543
// Create a module finder utility
544
public class ModuleDiscovery {
545
546
public static List<Module> findModules() {
547
List<Module> modules = new ArrayList<>();
548
549
// Look for modules in classpath
550
ServiceLoader<Module> loader = ServiceLoader.load(Module.class);
551
for (Module module : loader) {
552
modules.add(module);
553
}
554
555
// Add specific known modules
556
if (isClassAvailable("java.time.LocalDate")) {
557
modules.add(new JavaTimeModule());
558
}
559
560
if (isClassAvailable("org.joda.time.DateTime")) {
561
modules.add(new JodaModule());
562
}
563
564
return modules;
565
}
566
567
private static boolean isClassAvailable(String className) {
568
try {
569
Class.forName(className);
570
return true;
571
} catch (ClassNotFoundException e) {
572
return false;
573
}
574
}
575
576
public static ObjectMapper createConfiguredMapper() {
577
ObjectMapper mapper = new ObjectMapper();
578
579
// Auto-register discovered modules
580
List<Module> modules = findModules();
581
mapper.registerModules(modules);
582
583
return mapper;
584
}
585
}
586
587
// Usage
588
ObjectMapper mapper = ModuleDiscovery.createConfiguredMapper();
589
```
590
591
### Multi-Format Module
592
593
```java
594
// Module that adds support for multiple formats
595
public class MultiFormatModule extends SimpleModule {
596
597
public MultiFormatModule() {
598
super("MultiFormatModule");
599
600
// Date format support
601
addSerializer(Date.class, new FlexibleDateSerializer());
602
addDeserializer(Date.class, new FlexibleDateDeserializer());
603
604
// Number format support
605
addSerializer(BigDecimal.class, new ContextualBigDecimalSerializer());
606
addDeserializer(BigDecimal.class, new ContextualBigDecimalDeserializer());
607
}
608
609
// Date serializer that adapts to context
610
private static class FlexibleDateSerializer extends StdSerializer<Date> implements ContextualSerializer {
611
private final DateFormat dateFormat;
612
613
public FlexibleDateSerializer() {
614
this(null);
615
}
616
617
private FlexibleDateSerializer(DateFormat dateFormat) {
618
super(Date.class);
619
this.dateFormat = dateFormat;
620
}
621
622
@Override
623
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
624
if (property != null) {
625
JsonFormat.Value format = prov.getAnnotationIntrospector().findFormat(property.getMember());
626
if (format != null && format.hasPattern()) {
627
return new FlexibleDateSerializer(new SimpleDateFormat(format.getPattern()));
628
}
629
}
630
return this;
631
}
632
633
@Override
634
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
635
if (dateFormat != null) {
636
gen.writeString(dateFormat.format(value));
637
} else {
638
// Use timestamp or default format based on configuration
639
if (serializers.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) {
640
gen.writeNumber(value.getTime());
641
} else {
642
gen.writeString(value.toString());
643
}
644
}
645
}
646
}
647
}
648
```
649
650
### Module Testing Utilities
651
652
```java
653
// Utility class for testing modules
654
public class ModuleTestUtils {
655
656
public static void testModuleRegistration(Module module) {
657
ObjectMapper mapper = new ObjectMapper();
658
659
// Test module registration
660
mapper.registerModule(module);
661
662
// Verify module is registered
663
Set<Object> moduleIds = mapper.getRegisteredModuleIds();
664
boolean found = moduleIds.stream()
665
.anyMatch(id -> id.equals(module.getTypeId()) ||
666
id.toString().contains(module.getModuleName()));
667
668
if (!found) {
669
throw new IllegalStateException("Module not properly registered: " + module.getModuleName());
670
}
671
672
System.out.println("Module registered successfully: " + module.getModuleName());
673
}
674
675
public static void testSerializationRoundTrip(Module module, Object testObject, Class<?> type) {
676
ObjectMapper mapper = new ObjectMapper();
677
mapper.registerModule(module);
678
679
try {
680
// Serialize
681
String json = mapper.writeValueAsString(testObject);
682
System.out.println("Serialized: " + json);
683
684
// Deserialize
685
Object result = mapper.readValue(json, type);
686
687
// Basic equality check
688
if (!testObject.equals(result)) {
689
System.out.println("Warning: Serialization round-trip changed object");
690
System.out.println("Original: " + testObject);
691
System.out.println("Result: " + result);
692
} else {
693
System.out.println("Round-trip test passed for " + type.getSimpleName());
694
}
695
696
} catch (Exception e) {
697
throw new RuntimeException("Round-trip test failed for " + type.getSimpleName(), e);
698
}
699
}
700
}
701
702
// Usage in tests
703
@Test
704
public void testCustomModule() {
705
CustomTypesModule module = new CustomTypesModule();
706
ModuleTestUtils.testModuleRegistration(module);
707
708
Money testMoney = new Money("USD", new BigDecimal("123.45"));
709
ModuleTestUtils.testSerializationRoundTrip(module, testMoney, Money.class);
710
}
711
```
712
713
## Types
714
715
```java { .api }
716
// Module finder utilities
717
public class JacksonModules {
718
public static List<Module> findModules();
719
public static List<Module> findModules(ClassLoader classLoader);
720
public static void registerWellKnownModules(ObjectMapper mapper);
721
}
722
723
// Version information for modules
724
public class Version implements Comparable<Version>, Serializable {
725
public Version(int major, int minor, int patchLevel, String snapshotInfo, String groupId, String artifactId);
726
727
public int getMajorVersion();
728
public int getMinorVersion();
729
public int getPatchLevel();
730
public String getGroupId();
731
public String getArtifactId();
732
public boolean isSnapshot();
733
public boolean isUnknownVersion();
734
735
public static Version unknownVersion();
736
737
public String toString();
738
public int compareTo(Version other);
739
}
740
741
// Versioned interface for components
742
public interface Versioned {
743
Version version();
744
}
745
746
// Delegating deserializer base class
747
public abstract class DelegatingDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer, ResolvableDeserializer {
748
protected final JsonDeserializer<?> _delegatee;
749
750
protected DelegatingDeserializer(JsonDeserializer<?> delegatee);
751
752
protected abstract JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee);
753
754
public JsonDeserializer<?> getDelegatee();
755
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException;
756
public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue) throws IOException;
757
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException;
758
759
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException;
760
public void resolve(DeserializationContext ctxt) throws JsonMappingException;
761
}
762
763
// Delegating serializer base class
764
public abstract class DelegatingSerializer extends JsonSerializer<Object> implements ContextualSerializer, ResolvableSerializer {
765
protected final JsonSerializer<Object> _delegatee;
766
767
protected DelegatingSerializer(JsonSerializer<?> delegatee);
768
769
protected abstract JsonSerializer<?> newDelegatingInstance(JsonSerializer<?> newDelegatee);
770
771
public JsonSerializer<?> getDelegatee();
772
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException;
773
public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException;
774
775
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException;
776
public void resolve(SerializerProvider provider) throws JsonMappingException;
777
}
778
779
// Named type for subtype registration
780
public class NamedType implements Serializable {
781
public NamedType(Class<?> c);
782
public NamedType(Class<?> c, String name);
783
784
public Class<?> getType();
785
public String getName();
786
public boolean hasName();
787
788
public boolean equals(Object o);
789
public int hashCode();
790
public String toString();
791
}
792
```