Core annotations used for value types, used by Jackson data binding package.
—
Handle circular references and object identity with configurable ID generation and reference management.
Configure object identity for handling circular references and duplicate objects.
/**
* Configure object identity for reference handling
* @param property Identity property name in JSON
* @param generator ObjectIdGenerator implementation class
* @param resolver ObjectIdResolver implementation class
* @param scope Identity scope class (default: Object.class for global scope)
*/
@JsonIdentityInfo(String property = "@id",
Class<? extends ObjectIdGenerator<?>> generator,
Class<? extends ObjectIdResolver> resolver = SimpleObjectIdResolver.class,
Class<?> scope = Object.class)
public @interface JsonIdentityInfo;Usage Examples:
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
public class Department {
private Long id;
private String name;
private List<Employee> employees;
}
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "employeeId"
)
public class Employee {
private Long employeeId;
private String name;
private Department department;
}
// First occurrence: {"id": 1, "name": "Engineering", "employees": [{"employeeId": 100, "name": "John", "department": 1}]}
// Subsequent references: {"id": 1} or {"employeeId": 100}Configure whether to always serialize objects as identity references.
/**
* Configure whether to always serialize as identity reference
* @param alwaysAsId Always use ID instead of full object (default: false)
*/
@JsonIdentityReference(boolean alwaysAsId = false)
public @interface JsonIdentityReference;Usage Examples:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Category {
private Long id;
private String name;
@JsonIdentityReference(alwaysAsId = true)
private Category parentCategory; // Always serialized as just the ID
private List<Category> subcategories; // Full objects on first reference
}
// Result: {"id": 1, "name": "Electronics", "parentCategory": 5, "subcategories": [{"id": 2, "name": "Phones", "parentCategory": 1}]}Handle bidirectional relationships without infinite recursion.
/**
* Mark forward reference in bidirectional relationship
* @param value Reference name to match with @JsonBackReference
*/
@JsonManagedReference(String value = "defaultReference")
public @interface JsonManagedReference;
/**
* Mark back reference in bidirectional relationship
* @param value Reference name matching @JsonManagedReference
*/
@JsonBackReference(String value = "defaultReference")
public @interface JsonBackReference;Usage Examples:
public class Parent {
private String name;
@JsonManagedReference("parent-children")
private List<Child> children;
}
public class Child {
private String name;
@JsonBackReference("parent-children")
private Parent parent;
}
// Serialization: {"name": "ParentName", "children": [{"name": "Child1"}, {"name": "Child2"}]}
// Parent reference in children is omitted to prevent cycles
// Multiple reference types in same class:
public class Order {
@JsonManagedReference("order-items")
private List<OrderItem> items;
@JsonManagedReference("order-payments")
private List<Payment> payments;
}
public class OrderItem {
@JsonBackReference("order-items")
private Order order;
}
public class Payment {
@JsonBackReference("order-payments")
private Order order;
}Base class for generating object identifiers.
/**
* Base class for object ID generators
*/
public abstract class ObjectIdGenerator<T> {
/** Get the scope class for this generator */
public abstract Class<?> getScope();
/** Check if this generator can be used for another generator */
public abstract boolean canUseFor(ObjectIdGenerator<?> gen);
/** Create generator for specific scope */
public abstract ObjectIdGenerator<T> forScope(Class<?> scope);
/** Create new generator instance for serialization context */
public abstract ObjectIdGenerator<T> newForSerialization(Object context);
/** Generate ID for given object */
public abstract T generateId(Object forPojo);
/** Create IdKey for the given key */
public abstract IdKey key(Object key);
/**
* Key class for object identity mapping
*/
public static final class IdKey {
public final Class<?> type;
public final Class<?> scope;
public final Object key;
public IdKey(Class<?> type, Class<?> scope, Object key);
}
}Standard ObjectIdGenerator implementations.
/**
* Container for standard ObjectIdGenerator implementations
*/
public class ObjectIdGenerators {
/**
* Generator using integer sequence
*/
public static class IntSequenceGenerator extends ObjectIdGenerator<Integer> {
protected int _nextValue = 1;
public Integer generateId(Object forPojo) {
return _nextValue++;
}
}
/**
* Generator using UUID values
*/
public static class UUIDGenerator extends ObjectIdGenerator<UUID> {
public UUID generateId(Object forPojo) {
return UUID.randomUUID();
}
}
/**
* Generator using property value as ID
*/
public static abstract class PropertyGenerator extends ObjectIdGenerator<Object> {
// Uses existing property value as object ID
}
/**
* Generator using string values
*/
public static class StringIdGenerator extends ObjectIdGenerator<String> {
// Implementation for string-based IDs
}
/**
* No-op generator (no identity handling)
*/
public static abstract class None extends ObjectIdGenerator<Object> {
// Placeholder for no identity generation
}
}Interface for resolving object IDs to instances.
/**
* Interface for resolving object IDs to object instances
*/
public interface ObjectIdResolver {
/** Bind object ID to object instance */
void bindItem(ObjectIdGenerator.IdKey id, Object pojo);
/** Resolve ID to object instance */
Object resolveId(ObjectIdGenerator.IdKey id);
/** Create new resolver for deserialization context */
ObjectIdResolver newForDeserialization(Object context);
/** Check if this resolver can be used for another resolver type */
boolean canUseFor(ObjectIdResolver resolverType);
}Default HashMap-based ObjectIdResolver implementation.
/**
* Default ObjectIdResolver using HashMap for storage
*/
public class SimpleObjectIdResolver implements ObjectIdResolver {
protected Map<ObjectIdGenerator.IdKey, Object> _items;
public SimpleObjectIdResolver();
public void bindItem(ObjectIdGenerator.IdKey id, Object ob) {
if (_items == null) {
_items = new HashMap<>();
}
_items.put(id, ob);
}
public Object resolveId(ObjectIdGenerator.IdKey id) {
return (_items == null) ? null : _items.get(id);
}
public ObjectIdResolver newForDeserialization(Object context) {
return new SimpleObjectIdResolver();
}
public boolean canUseFor(ObjectIdResolver resolverType) {
return resolverType.getClass() == getClass();
}
}@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Department.class // Identity scoped to Department class
)
public class Employee {
private Long id;
private String name;
private Department department;
}
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Project.class // Different identity scope
)
public class Task {
private Long id; // Can have same ID as Employee because different scope
private String description;
}@JsonIdentityInfo(
generator = ObjectIdGenerators.UUIDGenerator.class,
property = "@uuid"
)
public class DistributedEntity {
private String name;
private String data;
// UUID automatically generated and used for identity
}
@JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "@seqId"
)
public class SequentialEntity {
private String content;
// Sequential integer IDs: @seqId: 1, 2, 3, etc.
}public class Node {
private String name;
@JsonManagedReference("node-children")
private List<Node> children;
@JsonBackReference("node-children")
private Node parent;
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "name"
)
@JsonIdentityReference(alwaysAsId = true)
private List<Node> references; // Cross-references to other nodes
}
// Handles both parent-child relationships and arbitrary cross-referencespublic class Company {
private String name;
@JsonManagedReference("company-departments")
private List<Department> departments;
}
public class Department {
private String name;
@JsonBackReference("company-departments")
private Company company;
@JsonManagedReference("department-employees")
private List<Employee> employees;
}
public class Employee {
private String name;
@JsonBackReference("department-employees")
private Department department;
@JsonIdentityReference(alwaysAsId = true)
private Employee manager; // Reference to another employee
}@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Manager.class, name = "manager"),
@JsonSubTypes.Type(value = Developer.class, name = "developer")
})
public abstract class Employee {
protected Long id;
protected String name;
}
public class Manager extends Employee {
@JsonIdentityReference(alwaysAsId = true)
private List<Employee> team;
}
public class Developer extends Employee {
@JsonIdentityReference(alwaysAsId = true)
private Manager manager;
}Install with Tessl CLI
npx tessl i tessl/maven-com-fasterxml-jackson-core--jackson-annotations