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

advanced-features.mddocs/

0

# Advanced Features

1

2

Jackson Databind provides sophisticated features for complex use cases including polymorphic type handling, object identity management, custom type handling, injectable values, and advanced property processing. These features enable handling of complex object graphs, type hierarchies, and specialized serialization/deserialization scenarios.

3

4

## Polymorphic Type Handling

5

6

Jackson supports polymorphic type handling through type information inclusion and resolution.

7

8

### TypeSerializer and TypeDeserializer

9

10

```java { .api }

11

public abstract class TypeSerializer {

12

// Type serialization methods for different JSON structures

13

public abstract void writeTypePrefixForScalar(Object value, JsonGenerator g) throws IOException;

14

public abstract void writeTypeSuffixForScalar(Object value, JsonGenerator g) throws IOException;

15

public abstract void writeTypePrefixForObject(Object value, JsonGenerator g) throws IOException;

16

public abstract void writeTypeSuffixForObject(Object value, JsonGenerator g) throws IOException;

17

public abstract void writeTypePrefixForArray(Object value, JsonGenerator g) throws IOException;

18

public abstract void writeTypeSuffixForArray(Object value, JsonGenerator g) throws IOException;

19

20

// Custom type serialization

21

public abstract void writeCustomTypePrefixForScalar(Object value, JsonGenerator g, String typeId) throws IOException;

22

public abstract void writeCustomTypeSuffixForScalar(Object value, JsonGenerator g, String typeId) throws IOException;

23

public abstract void writeCustomTypePrefixForObject(Object value, JsonGenerator g, String typeId) throws IOException;

24

public abstract void writeCustomTypeSuffixForObject(Object value, JsonGenerator g, String typeId) throws IOException;

25

public abstract void writeCustomTypePrefixForArray(Object value, JsonGenerator g, String typeId) throws IOException;

26

public abstract void writeCustomTypeSuffixForArray(Object value, JsonGenerator g, String typeId) throws IOException;

27

28

// Type property handling

29

public abstract JsonTypeInfo.As getTypeInclusion();

30

public abstract String getPropertyName();

31

public abstract TypeIdResolver getTypeIdResolver();

32

33

// Utility methods

34

public Class<?> getDefaultImpl();

35

}

36

37

public abstract class TypeDeserializer {

38

// Type deserialization methods

39

public abstract Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt) throws IOException;

40

public abstract Object deserializeTypedFromObject(JsonParser jp, DeserializationContext ctxt) throws IOException;

41

public abstract Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt) throws IOException;

42

public abstract Object deserializeTypedFromAny(JsonParser jp, DeserializationContext ctxt) throws IOException;

43

44

// Type information access

45

public abstract JsonTypeInfo.As getTypeInclusion();

46

public abstract String getPropertyName();

47

public abstract TypeIdResolver getTypeIdResolver();

48

49

// Default implementation handling

50

public Class<?> getDefaultImpl();

51

public abstract Object getTypeId();

52

}

53

```

54

55

### TypeIdResolver

56

57

```java { .api }

58

public interface TypeIdResolver {

59

// Initialization

60

void init(JavaType baseType);

61

62

// Type ID resolution

63

String idFromValue(Object value);

64

String idFromValueAndType(Object value, Class<?> suggestedType);

65

String idFromBaseType();

66

67

// Type from ID

68

JavaType typeFromId(DatabindContext context, String id) throws IOException;

69

70

// Description for error messages

71

String getDescForKnownTypeIds();

72

73

// Mechanism information

74

JsonTypeInfo.Id getMechanism();

75

}

76

```

77

78

### TypeResolverBuilder

79

80

```java { .api }

81

public interface TypeResolverBuilder<T extends TypeResolverBuilder<T>> {

82

// Configuration methods

83

T inclusion(JsonTypeInfo.As includeAs);

84

T typeProperty(String propName);

85

T typeIdVisibility(boolean isVisible);

86

T defaultImpl(Class<?> defaultImpl);

87

88

// Serializer/deserializer building

89

TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType baseType, Collection<NamedType> subtypes);

90

TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes);

91

}

92

```

93

94

### PolymorphicTypeValidator

95

96

```java { .api }

97

public abstract class PolymorphicTypeValidator implements Serializable {

98

// Validation methods

99

public abstract Validity validateBaseType(MapperConfig<?> config, JavaType baseType);

100

public abstract Validity validateSubClassName(MapperConfig<?> config, JavaType baseType, String subClassName) throws JsonMappingException;

101

public abstract Validity validateSubType(MapperConfig<?> config, JavaType baseType, JavaType subType) throws JsonMappingException;

102

103

// No-op validator

104

public static final PolymorphicTypeValidator NONE;

105

106

// Validity enum

107

public enum Validity {

108

ALLOWED,

109

DENIED,

110

INDETERMINATE;

111

}

112

}

113

114

public class BasicPolymorphicTypeValidator extends PolymorphicTypeValidator {

115

// Builder pattern for configuration

116

public static Builder builder();

117

118

public static class Builder {

119

// Allow specific types

120

public Builder allowIfSubType(Class<?> subType);

121

public Builder allowIfSubType(String subTypePattern);

122

public Builder allowIfSubTypeIsArray();

123

124

// Allow base types

125

public Builder allowIfBaseType(Class<?> baseType);

126

public Builder allowIfBaseType(String baseTypePattern);

127

128

// Deny specific types

129

public Builder denyForExactBaseType(Class<?> baseType);

130

131

// Build validator

132

public PolymorphicTypeValidator build();

133

}

134

}

135

```

136

137

## Object Identity Management

138

139

### ObjectIdGenerator

140

141

```java { .api }

142

public abstract class ObjectIdGenerator<T> implements Serializable {

143

// Scope and compatibility

144

public abstract Class<?> getScope();

145

public abstract boolean canUseFor(ObjectIdGenerator<?> gen);

146

public abstract ObjectIdGenerator<T> forScope(Class<?> scope);

147

public abstract ObjectIdGenerator<T> newForSerialization(Object context);

148

149

// ID generation and keys

150

public abstract IdKey key(Object key);

151

public abstract T generateId(Object forPojo);

152

153

// Generator type information

154

public boolean maySerializeAsObject();

155

public boolean isValidReferencePropertyName(String name, Object parser);

156

}

157

158

// Standard generators

159

public class ObjectIdGenerators {

160

public static class IntSequenceGenerator extends ObjectIdGenerator<Integer> {

161

public IntSequenceGenerator();

162

public IntSequenceGenerator(Class<?> scope, int initialValue);

163

164

public Class<?> getScope();

165

public boolean canUseFor(ObjectIdGenerator<?> gen);

166

public ObjectIdGenerator<Integer> forScope(Class<?> scope);

167

public ObjectIdGenerator<Integer> newForSerialization(Object context);

168

public IdKey key(Object key);

169

public Integer generateId(Object forPojo);

170

}

171

172

public static class PropertyGenerator extends ObjectIdGenerator<Object> {

173

public PropertyGenerator(Class<?> scope);

174

175

public Class<?> getScope();

176

public boolean canUseFor(ObjectIdGenerator<?> gen);

177

public ObjectIdGenerator<Object> forScope(Class<?> scope);

178

public ObjectIdGenerator<Object> newForSerialization(Object context);

179

public IdKey key(Object key);

180

public Object generateId(Object forPojo);

181

}

182

183

public static class StringIdGenerator extends ObjectIdGenerator<String> {

184

// Similar methods for String-based IDs

185

}

186

187

public static class UUIDGenerator extends ObjectIdGenerator<UUID> {

188

// Similar methods for UUID-based IDs

189

}

190

}

191

```

192

193

### ObjectIdResolver

194

195

```java { .api }

196

public interface ObjectIdResolver {

197

// Object resolution during deserialization

198

void bindItem(IdKey id, Object ob);

199

Object resolveId(IdKey id);

200

boolean canUseFor(ObjectIdResolver resolverType);

201

ObjectIdResolver newForDeserialization(Object context);

202

}

203

204

// Standard resolver

205

public class SimpleObjectIdResolver implements ObjectIdResolver {

206

public void bindItem(IdKey id, Object ob);

207

public Object resolveId(IdKey id);

208

public boolean canUseFor(ObjectIdResolver resolverType);

209

public ObjectIdResolver newForDeserialization(Object context);

210

}

211

```

212

213

### ObjectIdReader and ObjectIdWriter

214

215

```java { .api }

216

public class ObjectIdReader implements Serializable {

217

// ID property information

218

public final JavaType idType;

219

public final PropertyName propertyName;

220

public final ObjectIdGenerator<?> generator;

221

public final ObjectIdResolver resolver;

222

public final JsonDeserializer<Object> deserializer;

223

public final SettableBeanProperty idProperty;

224

225

// Construction

226

public ObjectIdReader(JavaType t, PropertyName propName, ObjectIdGenerator<?> gen, JsonDeserializer<?> deser, SettableBeanProperty idProp, ObjectIdResolver resolver);

227

228

// Factory methods

229

public static ObjectIdReader construct(JavaType idType, PropertyName propName, ObjectIdGenerator<?> generator, JsonDeserializer<?> deser, SettableBeanProperty idProp, ObjectIdResolver resolver);

230

231

// Modification methods

232

public ObjectIdReader withAlwaysAsId();

233

public ObjectIdReader withResolver(ObjectIdResolver resolver);

234

public ObjectIdReader withDeserializer(JsonDeserializer<?> deser);

235

}

236

237

public class ObjectIdWriter implements Serializable {

238

// ID generation information

239

public final JavaType idType;

240

public final PropertyName propertyName;

241

public final ObjectIdGenerator<?> generator;

242

public final JsonSerializer<Object> serializer;

243

public final boolean alwaysAsId;

244

245

// Construction

246

public ObjectIdWriter(JavaType t, PropertyName propName, ObjectIdGenerator<?> gen, JsonSerializer<?> ser, boolean alwaysAsId);

247

248

// Factory methods

249

public static ObjectIdWriter construct(JavaType idType, PropertyName propName, ObjectIdGenerator<?> generator, boolean alwaysAsId);

250

251

// Modification methods

252

public ObjectIdWriter withSerializer(JsonSerializer<?> ser);

253

public ObjectIdWriter withAlwaysAsId(boolean state);

254

}

255

```

256

257

## Injectable Values

258

259

### InjectableValues

260

261

```java { .api }

262

public abstract class InjectableValues {

263

// Value injection

264

public abstract Object findInjectableValue(Object valueId, DeserializationContext ctxt, BeanProperty forProperty, Object beanInstance) throws JsonMappingException;

265

266

// Standard implementation

267

public static class Std extends InjectableValues implements Serializable {

268

// Construction

269

public Std();

270

public Std(Map<String, Object> values);

271

272

// Value management

273

public Std addValue(String key, Object value);

274

public Std addValue(Class<?> key, Object value);

275

276

// Injection implementation

277

public Object findInjectableValue(Object valueId, DeserializationContext ctxt, BeanProperty forProperty, Object beanInstance);

278

}

279

}

280

```

281

282

## Custom Property Handling

283

284

### PropertyNamingStrategy

285

286

```java { .api }

287

public abstract class PropertyNamingStrategy implements Serializable {

288

// Property name transformation methods

289

public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName);

290

public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName);

291

public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName);

292

public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName);

293

}

294

295

public class PropertyNamingStrategies {

296

// Standard strategies

297

public static final PropertyNamingStrategy LOWER_CAMEL_CASE;

298

public static final PropertyNamingStrategy UPPER_CAMEL_CASE;

299

public static final PropertyNamingStrategy SNAKE_CASE;

300

public static final PropertyNamingStrategy UPPER_SNAKE_CASE;

301

public static final PropertyNamingStrategy LOWER_CASE;

302

public static final PropertyNamingStrategy KEBAB_CASE;

303

public static final PropertyNamingStrategy LOWER_DOT_CASE;

304

305

// Base implementation for custom strategies

306

public abstract static class NamingBase extends PropertyNamingStrategy {

307

public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName);

308

public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName);

309

public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName);

310

public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam, String defaultName);

311

312

// Abstract method for name transformation

313

public abstract String translate(String propertyName);

314

}

315

316

// Specific strategy implementations

317

public static class SnakeCaseStrategy extends NamingBase {

318

public String translate(String input);

319

}

320

321

public static class UpperCamelCaseStrategy extends NamingBase {

322

public String translate(String input);

323

}

324

325

public static class LowerCaseStrategy extends NamingBase {

326

public String translate(String input);

327

}

328

329

public static class KebabCaseStrategy extends NamingBase {

330

public String translate(String input);

331

}

332

333

public static class LowerDotCaseStrategy extends NamingBase {

334

public String translate(String input);

335

}

336

}

337

```

338

339

### AccessorNamingStrategy

340

341

```java { .api }

342

public abstract class AccessorNamingStrategy {

343

// Accessor method name analysis

344

public abstract String findNameForIsGetter(AnnotatedMethod am, String name);

345

public abstract String findNameForRegularGetter(AnnotatedMethod am, String name);

346

public abstract String findNameForMutator(AnnotatedMethod am, String name);

347

348

// Provider interface for strategy factories

349

public static abstract class Provider implements Serializable {

350

public abstract AccessorNamingStrategy forPOJO(MapperConfig<?> config, AnnotatedClass targetClass);

351

public abstract AccessorNamingStrategy forBuilder(MapperConfig<?> config, AnnotatedClass builderClass, BeanDescription valueTypeDesc);

352

public abstract AccessorNamingStrategy forRecord(MapperConfig<?> config, AnnotatedClass recordClass);

353

}

354

}

355

356

public class DefaultAccessorNamingStrategy extends AccessorNamingStrategy {

357

// Standard JavaBeans naming convention implementation

358

public String findNameForIsGetter(AnnotatedMethod am, String name);

359

public String findNameForRegularGetter(AnnotatedMethod am, String name);

360

public String findNameForMutator(AnnotatedMethod am, String name);

361

362

// Provider implementation

363

public static class Provider extends AccessorNamingStrategy.Provider {

364

public AccessorNamingStrategy forPOJO(MapperConfig<?> config, AnnotatedClass targetClass);

365

public AccessorNamingStrategy forBuilder(MapperConfig<?> config, AnnotatedClass builderClass, BeanDescription valueTypeDesc);

366

public AccessorNamingStrategy forRecord(MapperConfig<?> config, AnnotatedClass recordClass);

367

}

368

}

369

```

370

371

## Handler Instantiation

372

373

### HandlerInstantiator

374

375

```java { .api }

376

public abstract class HandlerInstantiator {

377

// Deserializer instantiation

378

public abstract JsonDeserializer<?> deserializerInstance(DeserializationConfig config, Annotated annotated, Class<?> deserClass);

379

public abstract KeyDeserializer keyDeserializerInstance(DeserializationConfig config, Annotated annotated, Class<?> keyDeserClass);

380

381

// Serializer instantiation

382

public abstract JsonSerializer<?> serializerInstance(SerializationConfig config, Annotated annotated, Class<?> serClass);

383

384

// Type handling

385

public abstract TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> config, Annotated annotated, Class<?> builderClass);

386

public abstract TypeIdResolver typeIdResolverInstance(MapperConfig<?> config, Annotated annotated, Class<?> resolverClass);

387

388

// Value instantiation

389

public ValueInstantiator valueInstantiatorInstance(MapperConfig<?> config, Annotated annotated, Class<?> instClass);

390

391

// Object ID generation

392

public ObjectIdGenerator<?> objectIdGeneratorInstance(MapperConfig<?> config, Annotated annotated, Class<?> implClass);

393

public ObjectIdResolver resolverIdGeneratorInstance(MapperConfig<?> config, Annotated annotated, Class<?> implClass);

394

395

// Converter instantiation

396

public Converter<?, ?> converterInstance(MapperConfig<?> config, Annotated annotated, Class<?> implClass);

397

398

// Filter instantiation

399

public VirtualBeanPropertyWriter virtualPropertyWriterInstance(MapperConfig<?> config, Class<?> implClass);

400

}

401

```

402

403

## Advanced Type Processing

404

405

### TypeModifier

406

407

```java { .api }

408

public abstract class TypeModifier {

409

// Type modification during construction

410

public abstract JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory);

411

}

412

```

413

414

### MixIn Support

415

416

MixIn annotations allow adding Jackson annotations to classes without modifying them.

417

418

```java { .api }

419

// Example MixIn interface

420

public interface PersonMixIn {

421

@JsonProperty("full_name")

422

String getName();

423

424

@JsonIgnore

425

String getPassword();

426

427

@JsonFormat(pattern = "yyyy-MM-dd")

428

Date getBirthDate();

429

}

430

431

// Usage

432

ObjectMapper mapper = new ObjectMapper();

433

mapper.addMixIn(Person.class, PersonMixIn.class);

434

435

// Now Person class will use annotations from PersonMixIn

436

```

437

438

## Usage Examples

439

440

### Polymorphic Type Handling

441

442

```java

443

// Base class with type information

444

@JsonTypeInfo(

445

use = JsonTypeInfo.Id.NAME,

446

include = JsonTypeInfo.As.PROPERTY,

447

property = "type"

448

)

449

@JsonSubTypes({

450

@JsonSubTypes.Type(value = Dog.class, name = "dog"),

451

@JsonSubTypes.Type(value = Cat.class, name = "cat"),

452

@JsonSubTypes.Type(value = Bird.class, name = "bird")

453

})

454

public abstract class Animal {

455

protected String name;

456

protected int age;

457

458

// getters/setters

459

}

460

461

@JsonTypeName("dog")

462

public class Dog extends Animal {

463

private String breed;

464

private boolean goodBoy = true;

465

466

// getters/setters

467

}

468

469

@JsonTypeName("cat")

470

public class Cat extends Animal {

471

private int livesRemaining = 9;

472

private boolean indoor;

473

474

// getters/setters

475

}

476

477

// Usage with polymorphic type validator

478

PolymorphicTypeValidator validator = BasicPolymorphicTypeValidator.builder()

479

.allowIfSubType("com.example.model.animals")

480

.allowIfBaseType(Animal.class)

481

.build();

482

483

ObjectMapper mapper = new ObjectMapper();

484

mapper.activateDefaultTyping(validator, ObjectMapper.DefaultTyping.NON_FINAL);

485

486

// Serialization includes type information

487

List<Animal> animals = Arrays.asList(

488

new Dog("Rex", 5, "Golden Retriever"),

489

new Cat("Whiskers", 3, true)

490

);

491

492

String json = mapper.writeValueAsString(animals);

493

// [{"type":"dog","name":"Rex","age":5,"breed":"Golden Retriever","goodBoy":true},

494

// {"type":"cat","name":"Whiskers","age":3,"livesRemaining":9,"indoor":true}]

495

496

// Deserialization creates correct subtype instances

497

List<Animal> parsed = mapper.readValue(json, new TypeReference<List<Animal>>() {});

498

System.out.println(parsed.get(0).getClass()); // Dog

499

System.out.println(parsed.get(1).getClass()); // Cat

500

```

501

502

### Object Identity Management

503

504

```java

505

// Using object identity to handle circular references

506

@JsonIdentityInfo(

507

generator = ObjectIdGenerators.PropertyGenerator.class,

508

property = "id"

509

)

510

public class Department {

511

private Long id;

512

private String name;

513

private List<Employee> employees = new ArrayList<>();

514

515

// getters/setters

516

}

517

518

@JsonIdentityInfo(

519

generator = ObjectIdGenerators.PropertyGenerator.class,

520

property = "employeeId"

521

)

522

public class Employee {

523

private Long employeeId;

524

private String name;

525

private Department department;

526

private Employee manager;

527

private List<Employee> subordinates = new ArrayList<>();

528

529

// getters/setters

530

}

531

532

// Usage

533

Department engineering = new Department();

534

engineering.setId(1L);

535

engineering.setName("Engineering");

536

537

Employee alice = new Employee();

538

alice.setEmployeeId(100L);

539

alice.setName("Alice");

540

alice.setDepartment(engineering);

541

542

Employee bob = new Employee();

543

bob.setEmployeeId(200L);

544

bob.setName("Bob");

545

bob.setDepartment(engineering);

546

bob.setManager(alice);

547

548

alice.getSubordinates().add(bob);

549

engineering.getEmployees().addAll(Arrays.asList(alice, bob));

550

551

ObjectMapper mapper = new ObjectMapper();

552

String json = mapper.writeValueAsString(engineering);

553

554

// JSON uses object IDs to represent relationships:

555

// {"id":1,"name":"Engineering","employees":[100,200]}

556

// with separate definitions for employees using their IDs

557

558

Department parsed = mapper.readValue(json, Department.class);

559

// Object references are properly restored

560

```

561

562

### Injectable Values

563

564

```java

565

// Service class to be injected

566

public class AuditService {

567

public void logAccess(String user, String operation) {

568

System.out.println("User " + user + " performed " + operation);

569

}

570

}

571

572

// POJO that uses injectable values

573

public class AuditableEntity {

574

private String data;

575

576

@JacksonInject("auditService")

577

private AuditService auditService;

578

579

@JacksonInject("currentUser")

580

private String currentUser;

581

582

@JsonCreator

583

public AuditableEntity(@JsonProperty("data") String data) {

584

this.data = data;

585

}

586

587

@JsonSetter("data")

588

public void setData(String data) {

589

if (auditService != null && currentUser != null) {

590

auditService.logAccess(currentUser, "data update");

591

}

592

this.data = data;

593

}

594

595

// getters/setters

596

}

597

598

// Usage

599

ObjectMapper mapper = new ObjectMapper();

600

601

// Set up injectable values

602

InjectableValues.Std injectableValues = new InjectableValues.Std();

603

injectableValues.addValue("auditService", new AuditService());

604

injectableValues.addValue("currentUser", "john.doe");

605

606

mapper.setInjectableValues(injectableValues);

607

608

// During deserialization, services are injected

609

String json = "{\"data\":\"sensitive information\"}";

610

AuditableEntity entity = mapper.readValue(json, AuditableEntity.class);

611

// AuditService and currentUser are injected automatically

612

613

entity.setData("updated information"); // Triggers audit logging

614

```

615

616

### Custom Property Naming Strategy

617

618

```java

619

// Custom naming strategy for API compatibility

620

public class LegacyApiNamingStrategy extends PropertyNamingStrategies.NamingBase {

621

622

// Map of property name translations

623

private static final Map<String, String> PROPERTY_MAPPINGS = Map.of(

624

"firstName", "first_name",

625

"lastName", "last_name",

626

"emailAddress", "email_addr",

627

"phoneNumber", "phone_num",

628

"createdAt", "create_time",

629

"updatedAt", "update_time"

630

);

631

632

@Override

633

public String translate(String propertyName) {

634

// Check for explicit mapping first

635

String mapped = PROPERTY_MAPPINGS.get(propertyName);

636

if (mapped != null) {

637

return mapped;

638

}

639

640

// Apply snake_case transformation with special rules

641

StringBuilder result = new StringBuilder();

642

for (int i = 0; i < propertyName.length(); i++) {

643

char c = propertyName.charAt(i);

644

if (Character.isUpperCase(c)) {

645

if (i > 0) {

646

result.append('_');

647

}

648

result.append(Character.toLowerCase(c));

649

} else {

650

result.append(c);

651

}

652

}

653

654

return result.toString();

655

}

656

}

657

658

// Usage

659

ObjectMapper mapper = new ObjectMapper();

660

mapper.setPropertyNamingStrategy(new LegacyApiNamingStrategy());

661

662

public class User {

663

private String firstName; // -> "first_name"

664

private String lastName; // -> "last_name"

665

private String emailAddress; // -> "email_addr"

666

private Date createdAt; // -> "create_time"

667

668

// getters/setters

669

}

670

```

671

672

### Advanced MixIn Usage

673

674

```java

675

// Original third-party class (cannot modify)

676

public class ExternalLibraryClass {

677

private String internalId;

678

private Date timestamp;

679

private Map<String, Object> properties;

680

681

// Only has getters, no setters

682

public String getInternalId() { return internalId; }

683

public Date getTimestamp() { return timestamp; }

684

public Map<String, Object> getProperties() { return properties; }

685

}

686

687

// MixIn to add Jackson annotations

688

public abstract class ExternalLibraryMixIn {

689

// Rename properties

690

@JsonProperty("id")

691

public abstract String getInternalId();

692

693

// Custom date format

694

@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")

695

public abstract Date getTimestamp();

696

697

// Unwrap properties map

698

@JsonAnyGetter

699

public abstract Map<String, Object> getProperties();

700

701

// Add constructor for deserialization

702

@JsonCreator

703

public ExternalLibraryMixIn(

704

@JsonProperty("id") String internalId,

705

@JsonProperty("timestamp") Date timestamp,

706

@JsonAnySetter Map<String, Object> properties) {}

707

}

708

709

// Custom deserializer to handle constructor limitation

710

public class ExternalLibraryDeserializer extends StdDeserializer<ExternalLibraryClass> {

711

712

public ExternalLibraryDeserializer() {

713

super(ExternalLibraryClass.class);

714

}

715

716

@Override

717

public ExternalLibraryClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {

718

JsonNode node = p.getCodec().readTree(p);

719

720

// Use reflection or factory methods to create instance

721

ExternalLibraryClass obj = createInstance();

722

723

// Set fields using reflection

724

setField(obj, "internalId", node.get("id").asText());

725

setField(obj, "timestamp", parseDate(node.get("timestamp").asText()));

726

727

// Handle additional properties

728

Map<String, Object> props = new HashMap<>();

729

Iterator<Map.Entry<String, JsonNode>> fields = node.fields();

730

while (fields.hasNext()) {

731

Map.Entry<String, JsonNode> field = fields.next();

732

if (!"id".equals(field.getKey()) && !"timestamp".equals(field.getKey())) {

733

props.put(field.getKey(), ctxt.readTreeAsValue(field.getValue(), Object.class));

734

}

735

}

736

setField(obj, "properties", props);

737

738

return obj;

739

}

740

741

// Helper methods for reflection

742

private ExternalLibraryClass createInstance() { /* implementation */ }

743

private void setField(Object obj, String fieldName, Object value) { /* implementation */ }

744

private Date parseDate(String dateStr) { /* implementation */ }

745

}

746

747

// Usage

748

ObjectMapper mapper = new ObjectMapper();

749

mapper.addMixIn(ExternalLibraryClass.class, ExternalLibraryMixIn.class);

750

751

SimpleModule module = new SimpleModule();

752

module.addDeserializer(ExternalLibraryClass.class, new ExternalLibraryDeserializer());

753

mapper.registerModule(module);

754

755

// Now ExternalLibraryClass can be serialized/deserialized with custom behavior

756

```

757

758

### Custom Handler Instantiator

759

760

```java

761

// Dependency injection integration

762

public class SpringHandlerInstantiator extends HandlerInstantiator {

763

private final ApplicationContext applicationContext;

764

765

public SpringHandlerInstantiator(ApplicationContext context) {

766

this.applicationContext = context;

767

}

768

769

@Override

770

public JsonDeserializer<?> deserializerInstance(DeserializationConfig config, Annotated annotated, Class<?> deserClass) {

771

// Try to get from Spring context first

772

try {

773

return applicationContext.getBean(deserClass);

774

} catch (NoSuchBeanDefinitionException e) {

775

// Fall back to default instantiation

776

return (JsonDeserializer<?>) ClassUtil.createInstance(deserClass, config.canOverrideAccessModifiers());

777

}

778

}

779

780

@Override

781

public JsonSerializer<?> serializerInstance(SerializationConfig config, Annotated annotated, Class<?> serClass) {

782

try {

783

return applicationContext.getBean(serClass);

784

} catch (NoSuchBeanDefinitionException e) {

785

return (JsonSerializer<?>) ClassUtil.createInstance(serClass, config.canOverrideAccessModifiers());

786

}

787

}

788

789

@Override

790

public ValueInstantiator valueInstantiatorInstance(MapperConfig<?> config, Annotated annotated, Class<?> instClass) {

791

try {

792

return applicationContext.getBean(instClass);

793

} catch (NoSuchBeanDefinitionException e) {

794

return (ValueInstantiator) ClassUtil.createInstance(instClass, config.canOverrideAccessModifiers());

795

}

796

}

797

798

// Implement other methods similarly

799

}

800

801

// Usage in Spring configuration

802

@Configuration

803

public class JacksonConfig {

804

805

@Bean

806

@Primary

807

public ObjectMapper objectMapper(ApplicationContext context) {

808

ObjectMapper mapper = new ObjectMapper();

809

mapper.setHandlerInstantiator(new SpringHandlerInstantiator(context));

810

return mapper;

811

}

812

}

813

814

// Now serializers/deserializers can be Spring beans with dependency injection

815

@Component

816

public class AuditingSerializer extends JsonSerializer<AuditableEntity> {

817

818

@Autowired

819

private AuditService auditService;

820

821

@Override

822

public void serialize(AuditableEntity value, JsonGenerator gen, SerializerProvider serializers) throws IOException {

823

auditService.logSerialization(value);

824

// Perform serialization

825

}

826

}

827

```

828

829

## Types

830

831

```java { .api }

832

// Subtype resolver interface

833

public abstract class SubtypeResolver {

834

public abstract Collection<NamedType> collectAndResolveSubtypesByClass(MapperConfig<?> config, AnnotatedMember property, JavaType baseType);

835

public abstract Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config, AnnotatedMember property, JavaType baseType);

836

public NamedType findTypeByName(Collection<NamedType> subtypes, String typeName);

837

}

838

839

// Standard subtype resolver

840

public class StdSubtypeResolver extends SubtypeResolver {

841

public Collection<NamedType> collectAndResolveSubtypesByClass(MapperConfig<?> config, AnnotatedMember property, JavaType baseType);

842

public Collection<NamedType> collectAndResolveSubtypesByTypeId(MapperConfig<?> config, AnnotatedMember property, JavaType baseType);

843

public void registerSubtypes(NamedType... types);

844

public void registerSubtypes(Class<?>... classes);

845

public void registerSubtypes(Collection<Class<?>> subtypes);

846

}

847

848

// Type resolver builder implementation

849

public class StdTypeResolverBuilder implements TypeResolverBuilder<StdTypeResolverBuilder> {

850

public StdTypeResolverBuilder inclusion(JsonTypeInfo.As includeAs);

851

public StdTypeResolverBuilder typeProperty(String propName);

852

public StdTypeResolverBuilder typeIdVisibility(boolean isVisible);

853

public StdTypeResolverBuilder defaultImpl(Class<?> defaultImpl);

854

public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType baseType, Collection<NamedType> subtypes);

855

public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes);

856

}

857

858

// Default type ID resolver

859

public abstract class TypeIdResolverBase implements TypeIdResolver {

860

protected final JavaType _baseType;

861

protected final TypeFactory _typeFactory;

862

863

protected TypeIdResolverBase();

864

protected TypeIdResolverBase(JavaType baseType, TypeFactory typeFactory);

865

866

public void init(JavaType bt);

867

public String idFromBaseType();

868

public String getDescForKnownTypeIds();

869

870

// Abstract methods for subclasses

871

public abstract JsonTypeInfo.Id getMechanism();

872

}

873

874

// Specific type ID resolver implementations

875

public class MinimalClassNameIdResolver extends TypeIdResolverBase {

876

public JsonTypeInfo.Id getMechanism();

877

public String idFromValue(Object value);

878

public String idFromValueAndType(Object value, Class<?> suggestedType);

879

public JavaType typeFromId(DatabindContext context, String id);

880

}

881

882

public class ClassNameIdResolver extends TypeIdResolverBase {

883

// Similar methods but uses full class names

884

}

885

886

// Object ID key class

887

public final class IdKey implements Serializable {

888

public IdKey(Class<?> scope, Class<?> type, Object key);

889

890

public Class<?> getScope();

891

public Class<?> getType();

892

public Object getKey();

893

894

public boolean equals(Object o);

895

public int hashCode();

896

public String toString();

897

}

898

899

// Writeable object ID for serialization

900

public final class WritableObjectId {

901

public WritableObjectId(ObjectIdGenerator<?> generator);

902

public WritableObjectId(ObjectIdGenerator<?> generator, Object id);

903

904

public Class<?> getScope();

905

public Object generateId(Object forPojo);

906

public void writeAsId(JsonGenerator gen, SerializerProvider provider, ObjectIdWriter w) throws IOException;

907

public void writeAsField(JsonGenerator gen, SerializerProvider provider, ObjectIdWriter w) throws IOException;

908

}

909

910

// Readable object ID for deserialization

911

public class ReadableObjectId {

912

public ReadableObjectId(Object id);

913

public ReadableObjectId bindItem(Object ob, Object id);

914

915

public Object getKey();

916

public Object getValue();

917

public Class<?> getScope();

918

public boolean hasReferringProperties();

919

public Iterator<ObjectIdReferenceProperty> referringProperties();

920

}

921

```

922

923

<function_calls>

924

<invoke name="TodoWrite">

925

<parameter name="todos">[{"content": "Create entry-point document (index.md) with overview and key API snippets", "status": "completed", "activeForm": "Creating entry-point document"}, {"content": "Create object-mapping.md sub-document covering ObjectMapper, ObjectReader, ObjectWriter", "status": "completed", "activeForm": "Creating object-mapping.md"}, {"content": "Create json-tree-model.md sub-document covering JsonNode hierarchy and tree manipulation", "status": "completed", "activeForm": "Creating json-tree-model.md"}, {"content": "Create type-system.md sub-document covering JavaType and type handling", "status": "completed", "activeForm": "Creating type-system.md"}, {"content": "Create serialization.md sub-document covering JsonSerializer and serialization framework", "status": "completed", "activeForm": "Creating serialization.md"}, {"content": "Create deserialization.md sub-document covering JsonDeserializer and deserialization framework", "status": "completed", "activeForm": "Creating deserialization.md"}, {"content": "Create configuration.md sub-document covering features, settings, and configuration", "status": "completed", "activeForm": "Creating configuration.md"}, {"content": "Create annotations.md sub-document covering Jackson databind annotations", "status": "completed", "activeForm": "Creating annotations.md"}, {"content": "Create modules.md sub-document covering Module system and extensibility", "status": "completed", "activeForm": "Creating modules.md"}, {"content": "Create advanced-features.md sub-document covering polymorphism, custom handlers, etc.", "status": "completed", "activeForm": "Creating advanced-features.md"}]