Xlang Serialization
Fory Go enables seamless data exchange with Java, Python, C++, Rust, JavaScript/TypeScript, C#, Swift, Dart, Scala, and Kotlin. This guide covers xlang compatibility and type mapping.
Create an Xlang Fory Instance
Go defaults to xlang mode with compatible schema evolution. Set the mode explicitly in xlang examples:
f := fory.New(fory.WithXlang(true))
Type Registration for Xlang
Use consistent type IDs across all languages:
Go
type User struct {
ID int64
Name string
}
f := fory.New(fory.WithXlang(true))
f.RegisterStruct(User{}, 1)
data, _ := f.Serialize(&User{ID: 1, Name: "Alice"})
Java
public class User {
public long id;
public String name;
}
Fory fory = Fory.builder().withXlang(true).build();
fory.register(User.class, 1);
User user = fory.deserialize(data, User.class);
Python
from dataclasses import dataclass
import pyfory
@dataclass
class User:
id: pyfory.Int64
name: str
fory = pyfory.Fory(xlang=True)
fory.register(User, type_id=1)
user = fory.deserialize(data)
Type Mapping
See Type Mapping Specification for detailed type mappings across all languages.
Field Ordering
Cross-language serialization requires consistent field ordering. Fory sorts fields by their snake_case names alphabetically.
Go field names are converted to snake_case for sorting:
type Example struct {
UserID int64 // -> user_id
FirstName string // -> first_name
Age int32 // -> age
}
// Sorted order: age, first_name, user_id
Ensure other languages use matching field names that produce the same snake_case ordering, or use field IDs for explicit control:
type Example struct {
UserID int64 `fory:"id=0"`
FirstName string `fory:"id=1"`
Age int32 `fory:"id=2"`
}
Examples
Go to Java
Go (Serializer):
type Order struct {
ID int64
Customer string
Total float64
Items []string
}
f := fory.New(fory.WithXlang(true))
f.RegisterStruct(Order{}, 1)
order := &Order{
ID: 12345,
Customer: "Alice",
Total: 99.99,
Items: []string{"Widget", "Gadget"},
}
data, _ := f.Serialize(order)
// Send 'data' to Java service
Java (Deserializer):
public class Order {
public long id;
public String customer;
public double total;
public List<String> items;
}
Fory fory = Fory.builder().withXlang(true).build();
fory.register(Order.class, 1);
Order order = fory.deserialize(data, Order.class);
Python to Go
Python (Serializer):
from dataclasses import dataclass
import pyfory
@dataclass
class Message:
id: pyfory.Int64
content: str
timestamp: pyfory.Int64
fory = pyfory.Fory(xlang=True)
fory.register(Message, type_id=1)
msg = Message(id=1, content="Hello from Python", timestamp=1234567890)
data = fory.serialize(msg)
Go (Deserializer):
type Message struct {
ID int64
Content string
Timestamp int64
}
f := fory.New(fory.WithXlang(true))
f.RegisterStruct(Message{}, 1)
var msg Message
f.Deserialize(data, &msg)
fmt.Println(msg.Content) // "Hello from Python"
Nested Structures
Cross-language nested structures require all types to be registered:
Lists and Dense Arrays
Go slices are ordinary list<T> carriers unless a field tag explicitly requests
the dense array<T> schema. Use array<T> only for one-dimensional bool or
numeric data.
| Fory schema | Go carrier and tag sketch |
|---|---|
list<int32> | []int32 / fory:"type=list(element=int32)" |
array<bool> | []bool / fory:"type=array(element=bool)" |
array<int8> | []int8 / fory:"type=array(element=int8)" |
array<int16> | []int16 / fory:"type=array(element=int16)" |
array<int32> | []int32 / fory:"type=array(element=int32)" |
array<int64> | []int64 / fory:"type=array(element=int64)" |
array<uint8> | []uint8 / fory:"type=array(element=uint8)" |
array<uint16> | []uint16 / fory:"type=array(element=uint16)" |
array<uint32> | []uint32 / fory:"type=array(element=uint32)" |
array<uint64> | []uint64 / fory:"type=array(element=uint64)" |
array<float16> | []float16.Float16 / type=array(element=float16) |
array<bfloat16> | []bfloat16.BFloat16 / type=array(element=bfloat16) |
array<float32> | []float32 / fory:"type=array(element=float32)" |
array<float64> | []float64 / fory:"type=array(element=float64)" |
Go:
type Address struct {
Street string
City string
Country string
}
type Company struct {
Name string
Address Address
}
f := fory.New(fory.WithXlang(true))
f.RegisterStruct(Address{}, 1)
f.RegisterStruct(Company{}, 2)
Java:
public class Address {
public String street;
public String city;
public String country;
}
public class Company {
public String name;
public Address address;
}
fory.register(Address.class, 1);
fory.register(Company.class, 2);
Common Issues
Field Name Mismatch
Go uses PascalCase, other languages may use camelCase or snake_case. Fields are matched by their snake_case conversion:
// Go
type User struct {
FirstName string // -> first_name
}
// Java - field name converted to snake_case must match
public class User {
public String firstName; // -> first_name (matches)
}
Type Interpretation
Go unsigned types map to Java signed types with the same bit pattern:
var value uint64 = 18446744073709551615 // Max uint64
Java's long holds the same bits but interprets as -1. Use Long.toUnsignedString() in Java if unsigned interpretation is needed.
Nil vs Null
Go nil slices/maps serialize differently based on configuration:
var slice []string = nil
// In xlang mode: serializes based on nullable configuration
Ensure other languages handle null appropriately.
Best Practices
- Use consistent type IDs: Same numeric ID for the same type across all languages
- Register all types: Including nested struct types
- Match field ordering: Use same snake_case names or explicit field IDs
- Test cross-language: Run integration tests early and often
- Handle type differences: Be aware of signed/unsigned interpretation differences