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"}]