Skip to main content
Version: dev

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, use ref=false to 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:

TypeOptionsDefault
int32varint, fixedvarint
uint32varint, fixedvarint
int64varint, fixed, taggedvarint
uint64varint, fixed, taggedvarint

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:

  1. Field name (alphabetically in snake_case)
  2. 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 (ref tag not set)

Xlang Mode:

  • Nullable: Only pointer types are nullable by default (slices and maps are NOT nullable)
  • Ref tracking: Disabled by default (ref tag 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

OptionDefaultHow to Enable
nullablefalseUse pointer types (*T)
reffalseAdd fory:"ref" tag
id-1Add fory:"id=N" tag

Best Practices

  1. Use - for sensitive data: Passwords, tokens, internal state
  2. Enable ref tracking for shared objects: When the same pointer appears multiple times
  3. Disable ref tracking for simple fields: Optimization when you know the field is unique
  4. Keep names consistent: Cross-language names should match
  5. 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
}