| title | sidebar_position | id | license |
|---|---|---|---|
Cross-Language Serialization |
8 |
cross_language |
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
|
Apache Fory™ supports seamless data exchange between Java and other languages (Python, Rust, Go, JavaScript, etc.) through the xlang serialization format. This enables multi-language microservices, polyglot data pipelines, and cross-platform data sharing.
To serialize data for consumption by other languages, use Language.XLANG mode:
import org.apache.fory.*;
import org.apache.fory.config.*;
// Create Fory instance with XLANG mode
Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true) // Enable reference tracking for complex graphs
.build();Types must be registered with consistent IDs or names across all languages. Fory supports two registration methods:
public record Person(String name, int age) {}
// Register with numeric ID - faster and more compact
fory.register(Person.class, 1);
Person person = new Person("Alice", 30);
byte[] bytes = fory.serialize(person);
// bytes can be deserialized by Python, Rust, Go, etc.Benefits: Faster serialization, smaller binary size Trade-offs: Requires coordination to avoid ID conflicts across teams/services
public record Person(String name, int age) {}
// Register with string name - more flexible
fory.register(Person.class, "example.Person");
Person person = new Person("Alice", 30);
byte[] bytes = fory.serialize(person);
// bytes can be deserialized by Python, Rust, Go, etc.Benefits: Less prone to conflicts, easier management across teams, no coordination needed Trade-offs: Slightly larger binary size due to string encoding
import org.apache.fory.*;
import org.apache.fory.config.*;
public record Person(String name, int age) {}
public class Example {
public static void main(String[] args) {
Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true)
.build();
// Register with consistent name
fory.register(Person.class, "example.Person");
Person person = new Person("Bob", 25);
byte[] bytes = fory.serialize(person);
// Send bytes to Python service via network/file/queue
}
}import pyfory
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: pyfory.int32
# Create Fory in xlang mode
fory = pyfory.Fory(xlang=True, ref=True)
# Register with the SAME name as Java
fory.register_type(Person, typename="example.Person")
# Deserialize bytes from Java
person = fory.deserialize(bytes_from_java)
print(f"{person.name}, {person.age}") # Output: Bob, 25Cross-language mode supports circular and shared references when reference tracking is enabled:
public class Node {
public String value;
public Node next;
public Node parent;
}
Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.withRefTracking(true) // Required for circular references
.build();
fory.register(Node.class, "example.Node");
// Create circular reference
Node node1 = new Node();
node1.value = "A";
Node node2 = new Node();
node2.value = "B";
node1.next = node2;
node2.parent = node1; // Circular reference
byte[] bytes = fory.serialize(node1);
// Python/Rust/Go can correctly deserialize this with circular references preservedNot all Java types have equivalents in other languages. When using xlang mode:
- Use primitive types (
int,long,double,String) for maximum compatibility - Use standard collections (
List,Map,Set) instead of language-specific ones - Avoid Java-specific types like
Optional,BigDecimal(unless the target language supports them) - See Type Mapping Guide for complete compatibility matrix
public record UserData(
String name, // ✅ Compatible
int age, // ✅ Compatible
List<String> tags, // ✅ Compatible
Map<String, Integer> scores // ✅ Compatible
) {}public record UserData(
Optional<String> name, // ❌ Not cross-language compatible
BigDecimal balance, // ❌ Limited support
EnumSet<Status> statuses // ❌ Java-specific collection
) {}Cross-language mode has additional overhead compared to Java-only mode:
- Type metadata encoding: Adds extra bytes per type
- Type resolution: Requires name/ID lookup during deserialization
For best performance:
- Use ID-based registration when possible (smaller encoding)
- Disable reference tracking if you don't need circular references (
withRefTracking(false)) - Use Java mode (
Language.JAVA) when only Java serialization is needed
- Consistent Registration: Ensure all services register types with identical IDs/names
- Version Compatibility: Use compatible mode for schema evolution across services
- Verify type is registered with same ID/name on both sides
- Check if type name has typos or case differences
- Ensure field types are compatible across languages
- Review Type Mapping Guide
- Verify both sides use
Language.XLANGmode - Ensure both sides have compatible Fory versions
- Cross-Language Serialization Specification
- Type Mapping Reference
- Python Cross-Language Guide
- Rust Cross-Language Guide
- Schema Evolution - Compatible mode
- Type Registration - Registration methods
- Row Format - Cross-language row format