title: Schema Metadata sidebar_position: 5 id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Fory Go uses struct tags to customize field-level serialization behavior. This allows fine-grained control over how individual fields are serialized.
The general syntax for Fory struct tags:
type MyStruct struct {
Field Type `fory:"option1,option2=value"`
}
Multiple options are separated by commas (,).
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:
Notes:
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.
Use nullable to control whether null flags are written for pointer, slice, map, or interface 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:
nullable=false, serializing a nil value will cause an erroroptional carrier fields. Native mode defaults pointer, slice, map, and interface fields to nullable.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:
*int, *string) cannot use this tagref=false (no reference tracking)WithTrackRef(false) is set, field ref tags are ignoredWithTrackRef(true) is set, use ref=false to disable for specific fieldsUse cases:
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 preservedUse type= to override inferred carrier semantics or nested value encoding:
type Foo struct { // Force general list protocol. Values []int32 `fory:"type=list"` // Override inner integer encoding for a general list FixedValues []int32 `fory:"type=list(element=int32(encoding=fixed))"` // Override nested map/list integer encoding Nested map[string][]*uint64 `fory:"type=map(value=list(element=uint64(encoding=tagged)))"` // Declare dense numeric array schema explicitly. Dense []int32 `fory:"type=array(element=int32)"` // Use array schema inside a map value. Packed map[string][]int32 `fory:"type=map(value=array(element=int32))"` }
Notes:
list(...), array(...), set(...), and map(...) are explicit container overrideslist(...) always uses list schema and never collapses into dense array schemaarray(element=...) requires a bool or numeric element domain and rejects nullable elements and scalar encoding modifiersMultiple 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,nullable=false,ref"`
}
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.
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.
Fields are serialized in a consistent order based on:
This ensures cross-language compatibility where field order matters.
Fory computes a hash of struct fields for version checking:
ErrKindHashMismatchStruct field changes affect the hash:
// These produce different hashes type V1 struct { UserID int64 } type V2 struct { UserId int64 // Different field name = different hash }
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 }
type CacheEntry struct { Key string Value *CachedData `fory:"ref"` // May be shared Metadata *Metadata `fory:"ref=false"` // Always unique ExpiresAt int64 }
type Document struct { ID int64 Title string Parent *Document `fory:"ref"` // May reference self or siblings Children []*Document `fory:"ref"` }
Invalid tags produce errors during registration:
type BadStruct struct { Field int `fory:"invalid=option=format"` } f := fory.New(fory.WithXlang(true)) err := f.RegisterStruct(BadStruct{}, 1) // Error: ErrKindInvalidTag
Field configuration behaves differently depending on the serialization mode:
Native Mode:
ref tag not set)Xlang Mode:
optional.Optional[T] fields are nullable by default (slices, maps, and interfaces are NOT nullable unless tagged)ref tag not set)You need to configure fields when:
*string, *int32)fory:"ref")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 }
| Option | Default | How to Enable |
|---|---|---|
nullable | Pointer and optional.Optional[T] fields in xlang mode; pointer, slice, map, and interface fields in native mode | Use fory:"nullable" or fory:"nullable=false" |
ref | false | Add fory:"ref" tag |
id | omitted | Add fory:"id=N" tag |
- for sensitive data: Passwords, tokens, internal statetype Rectangle struct { Width float64 Height float64 Area float64 `fory:"-"` // Computed, don't serialize } func (r *Rectangle) ComputeArea() { r.Area = r.Width * r.Height }
type TreeNode struct { Value string Parent *TreeNode `fory:"ref"` // Circular back-reference Children []*TreeNode `fory:"ref"` }
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 }