Field Configuration
This page explains how to configure field-level metadata for serialization in Java.
Overview
Apache Fory™ provides field-level configuration through annotations:
@ForyField: Configure field metadata (id, nullable, ref, dynamic)@Ignore: Exclude fields from serialization- Integer type annotations: Control integer encoding (varint, fixed, tagged, unsigned)
This enables:
- Tag IDs: Assign compact numeric IDs to reduce struct field meta size overhead for compatible mode
- Nullability: Control whether fields can be null
- Reference Tracking: Enable reference tracking for shared objects
- Field Skipping: Exclude fields from serialization
- Encoding Control: Specify how integers are encoded
- Polymorphism Control: Control type info writing for struct fields
Basic Syntax
Use annotations on fields:
import org.apache.fory.annotation.ForyField;
public class Person {
@ForyField(id = 0)
private String name;
@ForyField(id = 1)
private int age;
@ForyField(id = 2, nullable = true)
private String nickname;
}
The @ForyField Annotation
Use @ForyField to configure field-level metadata:
public class User {
@ForyField(id = 0)
private long id;
@ForyField(id = 1)
private String name;
@ForyField(id = 2, nullable = true)
private String email;
@ForyField(id = 3, ref = true)
private List<User> friends;
@ForyField(id = 4, dynamic = ForyField.Dynamic.TRUE)
private Object data;
}
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
id | int | -1 | Field tag ID (-1 = use field name) |
nullable | boolean | false | Whether the field can be null |
ref | boolean | false | Enable reference tracking |
dynamic | Dynamic | AUTO | Control polymorphism for struct fields |
Field ID (id)
Assigns a numeric ID to a field to minimize struct field meta size overhead for compatible mode:
public class User {
@ForyField(id = 0)
private long id;
@ForyField(id = 1)
private String name;
@ForyField(id = 2)
private int age;
}
Benefits:
- Smaller serialized size (numeric IDs vs field names in metadata)
- Reduced struct field meta overhead
- Allows renaming fields without breaking binary compatibility
Recommendation: It is recommended to configure field IDs for compatible mode since it reduces serialization cost.
Notes:
- IDs must be unique within a class
- IDs must be >= 0 (use -1 to use field name encoding, which is the default)
- If not specified, field name is used in metadata (larger overhead)
Without field IDs (field names used in metadata):
public class User {
private long id;
private String name;
}
Nullable Fields (nullable)
Use nullable = true for fields that can be null:
public class Record {
// Nullable string field
@ForyField(id = 0, nullable = true)
private String optionalName;
// Nullable Integer field (boxed type)
@ForyField(id = 1, nullable = true)
private Integer optionalCount;
// Non-nullable field (default)
@ForyField(id = 2)
private String requiredName;
}
Notes:
- Default is
nullable = false(non-nullable) - When
nullable = false, Fory skips writing the null flag (saves 1 byte) - Boxed types (
Integer,Long, etc.) that can be null should usenullable = true
Reference Tracking (ref)
Enable reference tracking for fields that may be shared or circular:
public class RefOuter {
// Both fields may point to the same inner object
@ForyField(id = 0, ref = true, nullable = true)
private RefInner inner1;
@ForyField(id = 1, ref = true, nullable = true)
private RefInner inner2;
}
public class CircularRef {
@ForyField(id = 0)
private String name;
// Self-referencing field for circular references
@ForyField(id = 1, ref = true, nullable = true)
private CircularRef selfRef;
}
Use Cases:
- Enable for fields that may be circular or shared
- When the same object is referenced from multiple fields
Notes:
- Default is
ref = false(no reference tracking) - When
ref = false, avoids IdentityMap overhead and skips ref tracking flag - Reference tracking only takes effect when global ref tracking is enabled
Dynamic (Polymorphism Control)
Controls polymorphism behavior for struct fields in cross-language serialization:
public class Container {
// AUTO: Interface/abstract types are dynamic, concrete types are not
@ForyField(id = 0, dynamic = ForyField.Dynamic.AUTO)
private Animal animal; // Interface - type info written
// FALSE: No type info written, uses declared type's serializer
@ForyField(id = 1, dynamic = ForyField.Dynamic.FALSE)
private Dog dog; // Concrete - no type info
// TRUE: Type info written to support runtime subtypes
@ForyField(id = 2, dynamic = ForyField.Dynamic.TRUE)
private Object data; // Force polymorphic
}
Options:
| Value | Description |
|---|---|
AUTO | Auto-detect: interface/abstract are dynamic, concrete types are not |
FALSE | No type info written, uses declared type's serializer directly |
TRUE | Type info written to support subtypes at runtime |
Skipping Fields
Using @Ignore
Exclude fields from serialization:
import org.apache.fory.annotation.Ignore;
public class User {
@ForyField(id = 0)
private long id;
@ForyField(id = 1)
private String name;
@Ignore
private String password; // Not serialized
@Ignore
private Object internalState; // Not serialized
}