Cross-Language Serialization
Apache Fory™ Dart serializes to the same binary format as the Java, Go, C#, Python, Rust, and Swift Fory runtimes. You can write a message in Dart and read it in Java — or any other direction — without any conversion layer.
Setup
Create a Fory instance as normal. There is no separate "cross-language mode" to enable in Dart:
final fory = Fory(); // or Fory(compatible: true) for schema evolution
The key requirement is that both sides register the same type using the same identity.
Registration Identity
The most important rule: use the same type identity on every side. You have two options:
Numeric ID
Simpler for small, tightly-coordinated teams:
// Dart
ModelsFory.register(fory, Person, id: 100);
Namespace + Type Name
Better when multiple teams define types independently:
// Dart
ModelsFory.register(
fory,
Person,
namespace: 'example',
typeName: 'Person',
);
Do not mix the two strategies for the same type across runtimes.
Dart to Java Example
Dart
import 'package:fory/fory.dart';
part 'person.fory.dart';
@ForyStruct()
class Person {
Person();
String name = '';
Int32 age = Int32(0);
}
final fory = Fory();
PersonFory.register(fory, Person, id: 100);
final bytes = fory.serialize(Person()
..name = 'Alice'
..age = Int32(30));
Java
Fory fory = Fory.builder()
.withLanguage(Language.XLANG)
.build();
fory.register(Person.class, 100);
Person value = (Person) fory.deserialize(bytesFromDart);
Dart to C# Example
Dart
final fory = Fory(compatible: true);
PersonFory.register(fory, Person, id: 100);
final bytes = fory.serialize(Person()
..name = 'Alice'
..age = Int32(30));
CSharp
[ForyObject]
public sealed class Person
{
public string Name { get; set; } = string.Empty;
public int Age { get; set; }
}
Fory fory = Fory.Builder()
.Compatible(true)
.Build();
fory.Register<Person>(100);
Person person = fory.Deserialize<Person>(payloadFromDart);
Dart to Go Example
Dart
final fory = Fory();
PersonFory.register(fory, Person, id: 100);
final bytes = fory.serialize(Person()
..name = 'Alice'
..age = Int32(30));
Go
type Person struct {
Name string
Age int32
}
f := fory.New(fory.WithXlang(true))
_ = f.RegisterStruct(Person{}, 100)
var person Person
_ = f.Deserialize(bytesFromDart, &person)
Field Matching Rules
Fory matches fields by name or by stable field ID. For robust cross-language interop:
- Use the same type identity on every side (same numeric ID or same
namespace + typeName). - Assign stable
@ForyField(id: ...)values to all fields before shipping the first payload. - Keep field names consistent or rely on IDs, since Dart typically uses
lowerCamelCasewhile Go usesPascalCasefor exported fields and C# often usesPascalCaseproperties. - Use compatible numeric types:
Int32in Dart for Javaint, Goint32, and C#int;doublein Dart for 64-bit floats;Float32for 32-bit. - Use
TimestampandLocalDatefor date/time fields rather than rawDateTime. - Validate real round trips across all languages before shipping.
Type Mapping Notes for Dart
Because Dart int is not itself a promise about the exact xlang wire width, prefer wrappers or numeric field annotations when exact cross-language interpretation matters:
Int32for xlangint32UInt32for xlanguint32Float16andFloat32for reduced-width floating pointTimestampandLocalDatefor explicit temporal semantics
See Supported Types and xlang type mapping.
Validation
Before relying on a cross-language contract in production, test a payload end-to-end through every runtime you support.
Run the Dart side:
dart run build_runner build --delete-conflicting-outputs
dart analyze
dart test