This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit eac02de3a26ed4736ec53de5514ba2ce7b64b5fc Author: chaokunyang <[email protected]> AuthorDate: Fri Jan 23 14:52:29 2026 +0000 🔄 synced local 'docs/compiler/' with remote 'docs/compiler/' --- docs/compiler/fdl-syntax.md | 37 +++++++++++++++++ docs/compiler/flatbuffers-idl.md | 40 ++++++++++++++++++- docs/compiler/generated-code.md | 86 ++++++++++++++++++++++++++++++++++++++++ docs/compiler/protobuf-idl.md | 26 +++++++----- 4 files changed, 176 insertions(+), 13 deletions(-) diff --git a/docs/compiler/fdl-syntax.md b/docs/compiler/fdl-syntax.md index 0455bb786..a6604c08b 100644 --- a/docs/compiler/fdl-syntax.md +++ b/docs/compiler/fdl-syntax.md @@ -672,6 +672,43 @@ message OtherMessage { - Within a message, you can reference nested types by simple name - From outside, use the qualified name (Parent.Child) +## Union Definition + +Unions define a value that can hold exactly one of several case types. + +### Basic Syntax + +```protobuf +union Animal [id=106] { + Dog dog = 1; + Cat cat = 2; +} +``` + +### Using a Union in a Message + +```protobuf +message Person [id=100] { + Animal pet = 1; + optional Animal favorite_pet = 2; +} +``` + +### Rules + +- Case IDs must be unique within the union +- Cases cannot be `optional`, `repeated`, or `ref` +- Union cases do not support field options +- Case types can be primitives, enums, messages, or other named types +- Union type IDs (`[id=...]`) are optional but recommended for cross-language use + +**Grammar:** + +``` +union_def := 'union' IDENTIFIER [type_options] '{' union_field* '}' +union_field := field_type IDENTIFIER '=' INTEGER ';' +``` + ## Field Definition Fields define the properties of a message. diff --git a/docs/compiler/flatbuffers-idl.md b/docs/compiler/flatbuffers-idl.md index 9f8e932be..e12ac1361 100644 --- a/docs/compiler/flatbuffers-idl.md +++ b/docs/compiler/flatbuffers-idl.md @@ -26,7 +26,7 @@ already have FlatBuffers schemas but want Fory-native serialization and codegen. ## Key Differences vs FDL - **Field numbering**: FlatBuffers fields have no explicit IDs; Fory assigns - sequential field numbers based on declaration order. + sequential field numbers based on declaration order, starting at 1. - **Tables vs structs**: FlatBuffers `table` maps to a Fory message with `evolving=true`; `struct` maps to `evolving=false`. - **Default values**: Parsed for compatibility but ignored in generated Fory @@ -35,7 +35,8 @@ already have FlatBuffers schemas but want Fory-native serialization and codegen. fields; FDL uses `[option=value]` inline syntax. - **Root type**: `root_type` is ignored because Fory does not require a root message to serialize. -- **Unions**: FlatBuffers `union` is not supported yet and raises an error. +- **Unions**: FlatBuffers `union` is translated into an FDL `union`. Case IDs + follow declaration order, starting at 1. ## Scalar Type Mapping @@ -56,6 +57,41 @@ already have FlatBuffers schemas but want Fory-native serialization and codegen. Vectors (`[T]`) map to Fory list types. +## Union Mapping + +FlatBuffers unions are converted to FDL unions and then to native union APIs in +each target language. + +**FlatBuffers:** + +```fbs +union Payload { + Note, + Metric +} + +table Container { + payload: Payload; +} +``` + +**FDL (conceptual):** + +```protobuf +union Payload { + Note note = 1; + Metric metric = 2; +} + +message Container { + Payload payload = 1; +} +``` + +Case IDs are derived from the declaration order in the `union`. The generated +case names are based on the type names (converted to each language's naming +convention). + ## Usage Compile a FlatBuffers schema directly: diff --git a/docs/compiler/generated-code.md b/docs/compiler/generated-code.md index 71d08269f..88b8674b3 100644 --- a/docs/compiler/generated-code.md +++ b/docs/compiler/generated-code.md @@ -182,6 +182,92 @@ FORY_STRUCT(SearchResponse, results); | Rust | Nested module | `search_response::Result` | | C++ | Nested classes | `SearchResponse::Result` | +## Union Generation + +FDL unions generate type-safe APIs with an explicit active case. This example is +based on `integration_tests/idl_tests/idl/addressbook.fdl`: + +```protobuf +package addressbook; + +message Dog [id=104] { + string name = 1; + int32 bark_volume = 2; +} + +message Cat [id=105] { + string name = 1; + int32 lives = 2; +} + +union Animal [id=106] { + Dog dog = 1; + Cat cat = 2; +} + +message Person [id=100] { + Animal pet = 8; +} +``` + +### Java + +```java +Animal pet = Animal.ofDog(new Dog()); +if (pet.hasDog()) { + Dog dog = pet.getDog(); +} +Animal.AnimalCase caseId = pet.getAnimalCase(); +``` + +### Python + +```python +pet = Animal.dog(Dog(name="Rex", bark_volume=5)) +if pet.is_dog(): + dog = pet.dog_value() +case_id = pet.case_id() +``` + +### Go + +```go +pet := DogAnimal(&Dog{Name: "Rex", BarkVolume: 5}) +if dog, ok := pet.AsDog(); ok { + _ = dog +} +_ = pet.Visit(AnimalVisitor{ + Dog: func(d *Dog) error { return nil }, +}) +``` + +### Rust + +```rust +let pet = Animal::Dog(Dog { + name: "Rex".into(), + bark_volume: 5, +}); +``` + +### C++ + +```cpp +addressbook::Animal pet = addressbook::Animal::dog( + addressbook::Dog{"Rex", 5}); +if (pet.is_dog()) { + const addressbook::Dog& dog = pet.dog(); +} +``` + +Generated registration helpers also register union types, for example: + +- Java: `fory.registerUnion(Animal.class, 106, new UnionSerializer(...))` +- Python: `fory.register_union(Animal, type_id=106, serializer=AnimalSerializer(fory))` +- Go: `f.RegisterUnion(...)` +- Rust: `fory.register_union::<Animal>(106)?` +- C++: `FORY_UNION(addressbook::Animal, ...)` + ## Java ### Enum Generation diff --git a/docs/compiler/protobuf-idl.md b/docs/compiler/protobuf-idl.md index 0971b4a92..2732b34bf 100644 --- a/docs/compiler/protobuf-idl.md +++ b/docs/compiler/protobuf-idl.md @@ -207,7 +207,7 @@ message TreeNode { | List | `repeated T` | `repeated T` | | Map | `map<K, V>` | `map<K, V>` | | Nullable | `optional T` (proto3) | `optional T` | -| Oneof | `oneof` | Not supported | +| Oneof | `oneof` | `union` (case id = field number) | | Any | `google.protobuf.Any` | Not supported | | Extensions | `extend` | Not supported | @@ -276,7 +276,7 @@ user.setAge(30); 1. **Building gRPC services**: Protobuf is the native format for gRPC 2. **Maximum backward compatibility**: Protobuf's unknown field handling is robust 3. **Schema evolution is critical**: Adding/removing fields across versions -4. **You need oneof/Any types**: Complex polymorphism requirements +4. **You need Any types**: Protobuf-specific dynamic payloads 5. **Human-readable debugging**: TextFormat and JSON transcoding available 6. **Ecosystem integration**: Wide tooling support (linting, documentation) @@ -346,21 +346,25 @@ message Person [id=101] { ```protobuf // Proto -message Result { - oneof result { - Success success = 1; - Error error = 2; +message Event { + oneof payload { + string text = 1; + int32 number = 2; } } ``` ```protobuf -// FDL - Use separate optional fields -message Result [id=102] { - optional Success success = 1; - optional Error error = 2; +// FDL - oneof becomes a nested union plus an optional field referencing it +message Event [id=102] { + union payload { + string text = 1; + int32 number = 2; + } + optional payload payload = 1; } -// Or model as sealed class hierarchy in generated code +// Case ids are preserved from the oneof field numbers +// The field number is the smallest case id in the oneof ``` **Well-known types:** --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
