Field Configuration
Fory Go uses struct tags to customize field-level serialization behavior. This allows fine-grained control over how individual fields are serialized.
Tag Syntax
The general syntax for Fory struct tags:
type MyStruct struct {
Field Type `fory:"option1,option2=value"`
}
Multiple options are separated by commas (,).
Available Tags
Field ID
Use id=N to assign a numeric ID to a field for compact encoding:
type User struct {
ID int64 `fory:"id=0"`
Name string `fory:"id=1"`
Age int32 `fory:"id=2"`
}
Benefits:
- Smaller serialized size (numeric IDs vs field names)
- Faster serialization/deserialization
- Required for optimal cross-language compatibility
Notes:
- IDs must be unique within a struct
- IDs must be >= 0
- If not specified, field name is used (larger payload)
Ignoring Fields
Use - to exclude a field from serialization:
type User struct {
ID int64
Name string
Password string `fory:"-"` // Not serialized
}
The Password field will not be included in serialized output and will remain at its zero value after deserialization.
Nullable
Use nullable to control whether null flags are written for pointer fields:
type Record struct {
// Write null flag for this field (allows nil values)
OptionalData *Data `fory:"nullable"`
// Skip null flag (field must not be nil)
RequiredData *Data `fory:"nullable=false"`
}
Notes:
- Only applies to pointer, slice, and map fields
- When
nullable=false, serializing a nil value will cause an error - Default is
false(no null flag written)
Reference Tracking
Control per-field reference tracking for slices, maps, or pointer to struct fields:
type Container struct {
// Enable reference tracking for this field
SharedData *Data `fory:"ref"`
// Disable reference tracking for this field
SimpleData *Data `fory:"ref=false"`
}
Notes:
- Applies to slices, maps, and pointer to struct fields
- Pointer to primitive types (e.g.,
*int,*string) cannot use this tag - Default is
ref=false(no reference tracking) - When global
WithTrackRef(false)is set, field ref tags are ignored - When global
WithTrackRef(true)is set, useref=falseto disable for specific fields
Use cases:
- Enable for fields that may be circular or shared
- Disable for fields that are always unique (optimization)
Encoding
Use encoding to control how numeric fields are encoded:
type Metrics struct {
// Variable-length encoding (default, smaller for small values)
Count int64 `fory:"encoding=varint"`
// Fixed-length encoding (consistent size)
Timestamp int64 `fory:"encoding=fixed"`
// Tagged encoding (includes type tag)
Value int64 `fory:"encoding=tagged"`
}
Supported encodings:
| Type | Options | Default |
|---|---|---|
int32 | varint, fixed | varint |
uint32 | varint, fixed | varint |
int64 | varint, fixed, tagged | varint |
uint64 | varint, fixed, tagged | varint |
When to use:
varint: Best for values that are often small (default)fixed: Best for values that use full range (e.g., timestamps, hashes)tagged: When type information needs to be preserved
Shorthand for int32/uint32:
Use compress as a convenience tag for int32/uint32 fields:
type Data struct {
SmallValue int32 `fory:"compress"` // Same as encoding=varint (default)
FixedValue uint32 `fory:"compress=false"` // Same as encoding=fixed
}
Combining Tags
Multiple tags can be combined using comma separator:
type Document struct {
ID int64 `fory:"id=0,encoding=fixed"`
Content string `fory:"id=1"`
Author *User `fory:"id=2,ref"`
}
Integration with Other Tags
Fory tags coexist with other struct tags:
type User struct {
ID int64 `json:"id" fory:"id=0"`
Name string `json:"name,omitempty" fory:"id=1"`
Password string `json:"-" fory:"-"`
}
Each tag namespace is independent.
Field Visibility
Only exported fields (starting with uppercase) are considered:
type User struct {
ID int64 // Serialized
Name string // Serialized
password string // NOT serialized (unexported, no tag needed)
}
Unexported fields are always ignored, regardless of tags.
Field Ordering
Fields are serialized in a consistent order based on:
- Field name (alphabetically in snake_case)
- Field type
This ensures cross-language compatibility where field order matters.
Struct Hash
Fory computes a hash of struct fields for version checking:
- Hash includes field names and types
- Hash is written to serialized data
- Mismatch triggers
ErrKindHashMismatch
Struct field changes affect the hash:
// These produce different hashes
type V1 struct {
UserID int64
}
type V2 struct {
UserId int64 // Different field name = different hash
}
Examples
API Response Struct
type APIResponse struct {
Status int32 `json:"status" fory:"id=0"`
Message string `json:"message" fory:"id=1"`
Data any `json:"data" fory:"id=2"`
Internal string `json:"-" fory:"-"` // Ignored in both JSON and Fory
}
Caching with Shared References
type CacheEntry struct {
Key string
Value *CachedData `fory:"ref"` // May be shared
Metadata *Metadata `fory:"ref=false"` // Always unique
ExpiresAt int64
}
Document with Circular References
type Document struct {
ID int64
Title string
Parent *Document `fory:"ref"` // May reference self or siblings
Children []*Document `fory:"ref"`
}
Tag Parsing Errors
Invalid tags produce errors during registration:
type BadStruct struct {
Field int `fory:"invalid=option=format"`
}
f := fory.New()
err := f.RegisterStruct(BadStruct{}, 1)
// Error: ErrKindInvalidTag
Native Mode vs Xlang Mode
Field configuration behaves differently depending on the serialization mode:
Native Mode:
- Nullable: Pointer, slice, map, and interface types are nullable by default
- Ref tracking: Disabled by default (
reftag not set)
Xlang Mode:
- Nullable: Only pointer types are nullable by default (slices and maps are NOT nullable)
- Ref tracking: Disabled by default (
reftag not set)
You need to configure fields when:
- A field can be nil (use pointer types like
*string,*int32) - A field needs reference tracking for shared/circular objects (use
fory:"ref") - You want to reduce metadata size (use field IDs with
fory:"id=N")
// Xlang mode: explicit configuration required
type User struct {
ID int64 `fory:"id=0"`
Name string `fory:"id=1"`
Email *string `fory:"id=2"` // Pointer type for nullable
Friend *User `fory:"id=3,ref"` // Must declare ref for shared objects
}
Default Values Summary
| Option | Default | How to Enable |
|---|---|---|
nullable | false | Use pointer types (*T) |
ref | false | Add fory:"ref" tag |
id | -1 | Add fory:"id=N" tag |
Best Practices
- Use
-for sensitive data: Passwords, tokens, internal state - Enable ref tracking for shared objects: When the same pointer appears multiple times
- Disable ref tracking for simple fields: Optimization when you know the field is unique
- Keep names consistent: Cross-language names should match
- Document tag usage: Especially for non-obvious configurations
Common Patterns
Ignoring Computed Fields
type Rectangle struct {
Width float64
Height float64
Area float64 `fory:"-"` // Computed, don't serialize
}
func (r *Rectangle) ComputeArea() {
r.Area = r.Width * r.Height
}
Circular Structure with Parent
type TreeNode struct {
Value string
Parent *TreeNode `fory:"ref"` // Circular back-reference
Children []*TreeNode `fory:"ref"`
}
Mixed Serialization Needs
type Session struct {
ID string
UserID int64
Token string `fory:"-"` // Security: don't serialize
User *User `fory:"ref"` // May be shared across sessions
CreatedAt int64
}