fory 0.17.0
fory: ^0.17.0 copied to clipboard
Apache Fory Dart runtime
Apache Fory™ Dart #
Apache Fory™ Dart is the Dart xlang runtime for Apache Fory™. It reads and writes Fory's cross-language wire format and is designed around generated serializers for annotated Dart models, with customized serializers available for advanced use cases.
Features #
- Cross-language serialization with the Fory xlang format
- Generated serializers for annotated structs and enums
- Compatible mode for schema evolution
- Optional reference tracking for shared and circular object graphs
- Manual serializers for external types, custom payloads, and unions
- Explicit xlang value wrappers such as
Int32,UInt32,Float16,Float32,LocalDate, andTimestamp
Getting Started #
Add fory to your package dependencies.
dependencies:
fory: ^0.17.0
dev_dependencies:
build_runner: ^2.4.13
Basic Usage #
Use @ForyStruct() for generated struct serializers and include the generated
part file.
import 'package:fory/fory.dart';
part 'person.fory.dart';
enum Color {
red,
blue,
}
@ForyStruct()
class Person {
Person();
String name = '';
Int32 age = Int32(0);
Color favoriteColor = Color.red;
List<String> tags = <String>[];
}
void main() {
final fory = Fory();
PersonFory.register(
fory,
Color,
namespace: 'example',
typeName: 'Color',
);
PersonFory.register(
fory,
Person,
namespace: 'example',
typeName: 'Person',
);
final person = Person()
..name = 'Ada'
..age = Int32(36)
..favoriteColor = Color.blue
..tags = <String>['engineer', 'mathematician'];
final bytes = fory.serialize(person);
final roundTrip = fory.deserialize<Person>(bytes);
print(roundTrip.name);
}
Generate the companion file before running the program:
dart run build_runner build --delete-conflicting-outputs
Type Registration #
Generated types register through the generated library namespace. The namespace
class is named <FileName>Fory based on the source file that contains the
annotated types.
PersonFory.register(fory, Person, id: 100);
Or use namespace and type name registration:
PersonFory.register(
fory,
Person,
namespace: 'example',
typeName: 'Person',
);
Exactly one registration mode is required:
id: ...namespace: ...andtypeName: ...
Keep the same registration identity on all runtimes that exchange the type.
Configuration #
final fory = Fory(
compatible: true,
maxDepth: 256,
maxCollectionSize: 1 << 20,
maxBinarySize: 64 * 1024 * 1024,
);
| Option | Default | Description |
|---|---|---|
compatible |
false |
Enables compatible struct encoding for schema evolution |
checkStructVersion |
true |
Validates struct version in schema-consistent mode |
maxDepth |
256 |
Maximum nesting depth per operation |
maxCollectionSize |
1 << 20 |
Maximum collection and map payload size |
maxBinarySize |
64 << 20 |
Maximum binary payload size |
Reference Tracking #
Enable root-level reference tracking only when the root value itself is a graph or container that needs shared-reference tracking.
final shared = String.fromCharCodes('shared'.codeUnits);
final bytes = fory.serialize(<Object?>[shared, shared], trackRef: true);
final roundTrip = fory.deserialize<List<Object?>>(bytes);
For generated structs, prefer field-level reference metadata:
@ForyStruct()
class NodeList {
NodeList();
@ForyField(ref: true)
List<Object?> values = <Object?>[];
}
Field Annotations #
@ForyField() controls per-field serialization behavior:
| Option | Description |
|---|---|
skip |
Skip the field during serialization |
id |
Stable field ID for compatible-mode evolution |
nullable |
Override nullability inference |
ref |
Enable reference tracking for this field |
dynamic |
Control whether runtime type metadata is written |
Customized Serializers #
Use Serializer<T> when a type cannot use generated struct support or when you
need custom wire behavior.
import 'package:fory/fory.dart';
final class Person {
Person(this.name, this.age);
final String name;
final int age;
}
final class PersonSerializer extends Serializer<Person> {
const PersonSerializer();
@override
void write(WriteContext context, Person value) {
final buffer = context.buffer;
buffer.writeUtf8(value.name);
buffer.writeInt64(value.age);
}
@override
Person read(ReadContext context) {
final buffer = context.buffer;
return Person(buffer.readUtf8(), buffer.readInt64());
}
}
void main() {
final fory = Fory();
fory.registerSerializer(
Person,
const PersonSerializer(),
namespace: 'example',
typeName: 'Person',
);
final bytes = fory.serialize(Person('Ada', 36));
final roundTrip = fory.deserialize<Person>(bytes);
print(roundTrip.name);
}
Type Mapping #
Dart has no native fixed-width 8/16/32-bit integer or single-precision float
types. Fory Dart provides thin wrapper types (Int8, Int16, Int32, UInt8,
UInt16, UInt32, Float16, Float32) imported from package:fory/fory.dart
to represent these xlang wire types.
| Fory xlang type | Dart type |
|---|---|
| bool | bool |
| int8 | fory.Int8 (wrapper) |
| int16 | fory.Int16 (wrapper) |
| int32 | fory.Int32 (wrapper) |
| int64 | int |
| float16 | fory.Float16 (wrapper) |
| float32 | fory.Float32 (wrapper) |
| float64 | double |
| string | String |
| binary | Uint8List |
| local_date | LocalDate |
| timestamp | Timestamp |
| list | List |
| set | Set |
| map | Map |
| enum | enum |
| named_struct | class |
| bool_array | List<bool> |
| int8_array | Int8List |
| int16_array | Int16List |
| int32_array | Int32List |
| int64_array | Int64List |
| float32_array | Float32List |
| float64_array | Float64List |
Public API #
The main exported API includes:
Fory— main serialization facadeConfig— runtime configurationForyStruct,ForyField— struct annotationsForyUnion— union type annotationSerializer,UnionSerializer,EnumSerializer— serializer base classesBuffer,WriteContext,ReadContext— low-level I/OTypeSpec,ListType,MapType,ValueType— nested container type annotationsInt32Type,Int64Type,Uint32Type,Uint64Type— numeric encoding overrides- Numeric wrappers:
Int8,Int16,Int32,UInt8,UInt16,UInt32,Float16,Float32 - Temporal wrappers:
LocalDate,Timestamp
Cross-Language Notes #
- The Dart runtime only supports xlang payloads.
- Register user-defined types before serialization or deserialization.
- Keep numeric IDs or
namespace + typeNamemappings consistent across languages. - Use wrappers or numeric field annotations when the exact xlang wire type matters.
For the xlang wire format and type mapping details, see the Apache Fory specification.