title: Xlang Serialization sidebar_position: 2 id: xlang_serialization 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 enables seamless data exchange with Java, Python, C++, Rust, and JavaScript. This guide covers xlang compatibility and type mapping.
Go defaults to xlang mode with compatible schema evolution. Set the mode explicitly in xlang examples:
f := fory.New(fory.WithXlang(true))
Use consistent type IDs across all languages:
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"})
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);
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)
See Type Mapping Specification for detailed type mappings across all languages.
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"`
}
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 (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"
Cross-language nested structures require all types to be registered:
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);
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) }
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.
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.