or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdannotations.mdconfiguration.mddeserialization.mdindex.mdjson-tree-model.mdmodules.mdobject-mapping.mdserialization.mdtype-system.md

modules.mddocs/

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

```