0
# Object Identity and References
1
2
Handle circular references and object identity with configurable ID generation and reference management.
3
4
## Capabilities
5
6
### JsonIdentityInfo
7
8
Configure object identity for handling circular references and duplicate objects.
9
10
```java { .api }
11
/**
12
* Configure object identity for reference handling
13
* @param property Identity property name in JSON
14
* @param generator ObjectIdGenerator implementation class
15
* @param resolver ObjectIdResolver implementation class
16
* @param scope Identity scope class (default: Object.class for global scope)
17
*/
18
@JsonIdentityInfo(String property = "@id",
19
Class<? extends ObjectIdGenerator<?>> generator,
20
Class<? extends ObjectIdResolver> resolver = SimpleObjectIdResolver.class,
21
Class<?> scope = Object.class)
22
public @interface JsonIdentityInfo;
23
```
24
25
**Usage Examples:**
26
27
```java
28
@JsonIdentityInfo(
29
generator = ObjectIdGenerators.PropertyGenerator.class,
30
property = "id"
31
)
32
public class Department {
33
private Long id;
34
private String name;
35
private List<Employee> employees;
36
}
37
38
@JsonIdentityInfo(
39
generator = ObjectIdGenerators.PropertyGenerator.class,
40
property = "employeeId"
41
)
42
public class Employee {
43
private Long employeeId;
44
private String name;
45
private Department department;
46
}
47
48
// First occurrence: {"id": 1, "name": "Engineering", "employees": [{"employeeId": 100, "name": "John", "department": 1}]}
49
// Subsequent references: {"id": 1} or {"employeeId": 100}
50
```
51
52
### JsonIdentityReference
53
54
Configure whether to always serialize objects as identity references.
55
56
```java { .api }
57
/**
58
* Configure whether to always serialize as identity reference
59
* @param alwaysAsId Always use ID instead of full object (default: false)
60
*/
61
@JsonIdentityReference(boolean alwaysAsId = false)
62
public @interface JsonIdentityReference;
63
```
64
65
**Usage Examples:**
66
67
```java
68
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
69
public class Category {
70
private Long id;
71
private String name;
72
73
@JsonIdentityReference(alwaysAsId = true)
74
private Category parentCategory; // Always serialized as just the ID
75
76
private List<Category> subcategories; // Full objects on first reference
77
}
78
79
// Result: {"id": 1, "name": "Electronics", "parentCategory": 5, "subcategories": [{"id": 2, "name": "Phones", "parentCategory": 1}]}
80
```
81
82
### JsonManagedReference and JsonBackReference
83
84
Handle bidirectional relationships without infinite recursion.
85
86
```java { .api }
87
/**
88
* Mark forward reference in bidirectional relationship
89
* @param value Reference name to match with @JsonBackReference
90
*/
91
@JsonManagedReference(String value = "defaultReference")
92
public @interface JsonManagedReference;
93
94
/**
95
* Mark back reference in bidirectional relationship
96
* @param value Reference name matching @JsonManagedReference
97
*/
98
@JsonBackReference(String value = "defaultReference")
99
public @interface JsonBackReference;
100
```
101
102
**Usage Examples:**
103
104
```java
105
public class Parent {
106
private String name;
107
108
@JsonManagedReference("parent-children")
109
private List<Child> children;
110
}
111
112
public class Child {
113
private String name;
114
115
@JsonBackReference("parent-children")
116
private Parent parent;
117
}
118
119
// Serialization: {"name": "ParentName", "children": [{"name": "Child1"}, {"name": "Child2"}]}
120
// Parent reference in children is omitted to prevent cycles
121
122
// Multiple reference types in same class:
123
public class Order {
124
@JsonManagedReference("order-items")
125
private List<OrderItem> items;
126
127
@JsonManagedReference("order-payments")
128
private List<Payment> payments;
129
}
130
131
public class OrderItem {
132
@JsonBackReference("order-items")
133
private Order order;
134
}
135
136
public class Payment {
137
@JsonBackReference("order-payments")
138
private Order order;
139
}
140
```
141
142
## Object Identity System Components
143
144
### ObjectIdGenerator
145
146
Base class for generating object identifiers.
147
148
```java { .api }
149
/**
150
* Base class for object ID generators
151
*/
152
public abstract class ObjectIdGenerator<T> {
153
154
/** Get the scope class for this generator */
155
public abstract Class<?> getScope();
156
157
/** Check if this generator can be used for another generator */
158
public abstract boolean canUseFor(ObjectIdGenerator<?> gen);
159
160
/** Create generator for specific scope */
161
public abstract ObjectIdGenerator<T> forScope(Class<?> scope);
162
163
/** Create new generator instance for serialization context */
164
public abstract ObjectIdGenerator<T> newForSerialization(Object context);
165
166
/** Generate ID for given object */
167
public abstract T generateId(Object forPojo);
168
169
/** Create IdKey for the given key */
170
public abstract IdKey key(Object key);
171
172
/**
173
* Key class for object identity mapping
174
*/
175
public static final class IdKey {
176
public final Class<?> type;
177
public final Class<?> scope;
178
public final Object key;
179
180
public IdKey(Class<?> type, Class<?> scope, Object key);
181
}
182
}
183
```
184
185
### ObjectIdGenerators
186
187
Standard ObjectIdGenerator implementations.
188
189
```java { .api }
190
/**
191
* Container for standard ObjectIdGenerator implementations
192
*/
193
public class ObjectIdGenerators {
194
195
/**
196
* Generator using integer sequence
197
*/
198
public static class IntSequenceGenerator extends ObjectIdGenerator<Integer> {
199
protected int _nextValue = 1;
200
201
public Integer generateId(Object forPojo) {
202
return _nextValue++;
203
}
204
}
205
206
/**
207
* Generator using UUID values
208
*/
209
public static class UUIDGenerator extends ObjectIdGenerator<UUID> {
210
public UUID generateId(Object forPojo) {
211
return UUID.randomUUID();
212
}
213
}
214
215
/**
216
* Generator using property value as ID
217
*/
218
public static abstract class PropertyGenerator extends ObjectIdGenerator<Object> {
219
// Uses existing property value as object ID
220
}
221
222
/**
223
* Generator using string values
224
*/
225
public static class StringIdGenerator extends ObjectIdGenerator<String> {
226
// Implementation for string-based IDs
227
}
228
229
/**
230
* No-op generator (no identity handling)
231
*/
232
public static abstract class None extends ObjectIdGenerator<Object> {
233
// Placeholder for no identity generation
234
}
235
}
236
```
237
238
### ObjectIdResolver
239
240
Interface for resolving object IDs to instances.
241
242
```java { .api }
243
/**
244
* Interface for resolving object IDs to object instances
245
*/
246
public interface ObjectIdResolver {
247
248
/** Bind object ID to object instance */
249
void bindItem(ObjectIdGenerator.IdKey id, Object pojo);
250
251
/** Resolve ID to object instance */
252
Object resolveId(ObjectIdGenerator.IdKey id);
253
254
/** Create new resolver for deserialization context */
255
ObjectIdResolver newForDeserialization(Object context);
256
257
/** Check if this resolver can be used for another resolver type */
258
boolean canUseFor(ObjectIdResolver resolverType);
259
}
260
```
261
262
### SimpleObjectIdResolver
263
264
Default HashMap-based ObjectIdResolver implementation.
265
266
```java { .api }
267
/**
268
* Default ObjectIdResolver using HashMap for storage
269
*/
270
public class SimpleObjectIdResolver implements ObjectIdResolver {
271
272
protected Map<ObjectIdGenerator.IdKey, Object> _items;
273
274
public SimpleObjectIdResolver();
275
276
public void bindItem(ObjectIdGenerator.IdKey id, Object ob) {
277
if (_items == null) {
278
_items = new HashMap<>();
279
}
280
_items.put(id, ob);
281
}
282
283
public Object resolveId(ObjectIdGenerator.IdKey id) {
284
return (_items == null) ? null : _items.get(id);
285
}
286
287
public ObjectIdResolver newForDeserialization(Object context) {
288
return new SimpleObjectIdResolver();
289
}
290
291
public boolean canUseFor(ObjectIdResolver resolverType) {
292
return resolverType.getClass() == getClass();
293
}
294
}
295
```
296
297
## Advanced Identity Patterns
298
299
### Scoped Identity
300
301
```java
302
@JsonIdentityInfo(
303
generator = ObjectIdGenerators.PropertyGenerator.class,
304
property = "id",
305
scope = Department.class // Identity scoped to Department class
306
)
307
public class Employee {
308
private Long id;
309
private String name;
310
private Department department;
311
}
312
313
@JsonIdentityInfo(
314
generator = ObjectIdGenerators.PropertyGenerator.class,
315
property = "id",
316
scope = Project.class // Different identity scope
317
)
318
public class Task {
319
private Long id; // Can have same ID as Employee because different scope
320
private String description;
321
}
322
```
323
324
### Custom ID Generation
325
326
```java
327
@JsonIdentityInfo(
328
generator = ObjectIdGenerators.UUIDGenerator.class,
329
property = "@uuid"
330
)
331
public class DistributedEntity {
332
private String name;
333
private String data;
334
335
// UUID automatically generated and used for identity
336
}
337
338
@JsonIdentityInfo(
339
generator = ObjectIdGenerators.IntSequenceGenerator.class,
340
property = "@seqId"
341
)
342
public class SequentialEntity {
343
private String content;
344
345
// Sequential integer IDs: @seqId: 1, 2, 3, etc.
346
}
347
```
348
349
### Complex Circular References
350
351
```java
352
public class Node {
353
private String name;
354
355
@JsonManagedReference("node-children")
356
private List<Node> children;
357
358
@JsonBackReference("node-children")
359
private Node parent;
360
361
@JsonIdentityInfo(
362
generator = ObjectIdGenerators.PropertyGenerator.class,
363
property = "name"
364
)
365
@JsonIdentityReference(alwaysAsId = true)
366
private List<Node> references; // Cross-references to other nodes
367
}
368
369
// Handles both parent-child relationships and arbitrary cross-references
370
```
371
372
### Multi-level Relationships
373
374
```java
375
public class Company {
376
private String name;
377
378
@JsonManagedReference("company-departments")
379
private List<Department> departments;
380
}
381
382
public class Department {
383
private String name;
384
385
@JsonBackReference("company-departments")
386
private Company company;
387
388
@JsonManagedReference("department-employees")
389
private List<Employee> employees;
390
}
391
392
public class Employee {
393
private String name;
394
395
@JsonBackReference("department-employees")
396
private Department department;
397
398
@JsonIdentityReference(alwaysAsId = true)
399
private Employee manager; // Reference to another employee
400
}
401
```
402
403
### Identity with Inheritance
404
405
```java
406
@JsonIdentityInfo(
407
generator = ObjectIdGenerators.PropertyGenerator.class,
408
property = "id"
409
)
410
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
411
@JsonSubTypes({
412
@JsonSubTypes.Type(value = Manager.class, name = "manager"),
413
@JsonSubTypes.Type(value = Developer.class, name = "developer")
414
})
415
public abstract class Employee {
416
protected Long id;
417
protected String name;
418
}
419
420
public class Manager extends Employee {
421
@JsonIdentityReference(alwaysAsId = true)
422
private List<Employee> team;
423
}
424
425
public class Developer extends Employee {
426
@JsonIdentityReference(alwaysAsId = true)
427
private Manager manager;
428
}
429
```