This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch rewrite_docs in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit 370819beb1b4d46a67070979a25c06b4b1192aa7 Author: 慕白 <[email protected]> AuthorDate: Sun May 17 22:36:12 2026 +0800 docs: fix start guide modes and benchmark assets --- docs/introduction/benchmark.md | 4 +- docs/start/install.md | 18 + docs/start/usage.md | 465 ++++++++----------- .../current/introduction/benchmark.md | 4 +- .../current/start/install.md | 18 + .../current/start/usage.md | 493 ++++++++------------- .../version-0.17/introduction/benchmark.md | 4 +- .../version-0.17/start/install.md | 18 + .../version-0.17/start/usage.md | 493 ++++++++------------- .../version-0.17/introduction/benchmark.md | 4 +- versioned_docs/version-0.17/start/install.md | 18 + versioned_docs/version-0.17/start/usage.md | 465 ++++++++----------- 12 files changed, 820 insertions(+), 1184 deletions(-) diff --git a/docs/introduction/benchmark.md b/docs/introduction/benchmark.md index eb97295449..515ae697aa 100644 --- a/docs/introduction/benchmark.md +++ b/docs/introduction/benchmark.md @@ -92,7 +92,7 @@ report for details: https://fory.apache.org/docs/benchmarks/swift/ Fory JavaScript demonstrates strong performance compared to Protocol Buffers and JSON across representative Node.js workloads. -<img src="../benchmarks/javascript/throughput.png" width="90%"/> + Note: Results depend on hardware, dataset, and runtime versions. See the [JavaScript benchmark report](../benchmarks/javascript/README.md) for details. @@ -102,7 +102,7 @@ Note: Results depend on hardware, dataset, and runtime versions. See the Fory Dart demonstrates strong performance compared to Protocol Buffers across representative object and list workloads. -<img src="../benchmarks/dart/throughput.png" width="90%"/> + Note: Results depend on hardware, dataset, and runtime versions. See the [Dart benchmark report](../benchmarks/dart/README.md) for details. diff --git a/docs/start/install.md b/docs/start/install.md index 5c3d3e37fe..12249698b3 100644 --- a/docs/start/install.md +++ b/docs/start/install.md @@ -134,6 +134,24 @@ cd packages/hps npm run build ``` +## Dart + +Add Apache Fory™ Dart to `pubspec.yaml`: + +```yaml +dependencies: + fory: ^0.17.0 + +dev_dependencies: + build_runner: ^2.4.13 +``` + +Generate serializers after defining annotated types: + +```bash +dart run build_runner build --delete-conflicting-outputs +``` + ## C\# Install the `Apache.Fory` NuGet package. It includes both the runtime and the source generator for `[ForyObject]` types. diff --git a/docs/start/usage.md b/docs/start/usage.md index 4fa4a2f0b0..58481ca792 100644 --- a/docs/start/usage.md +++ b/docs/start/usage.md @@ -6,55 +6,44 @@ sidebar_position: 1 This section provides quick examples for getting started with Apache Fory™. -## Native Serialization +## Choose A Mode -**Always use native mode when working with a single language.** Native mode delivers optimal performance by avoiding the type metadata overhead required for cross-language compatibility. +Apache Fory™ has two wire modes: -Xlang mode introduces additional metadata encoding costs and restricts serialization to types that are common across all supported languages. Language-specific types will be rejected during serialization in xlang mode. +- **Xlang mode** is the default and the portable format for payloads shared across languages. Use it for cross-language services and for runtimes that expose only xlang mode: Dart, JavaScript/TypeScript, C#, and Swift. +- **Native mode** is selected with `xlang=false` or the equivalent builder option in Java, Scala, Kotlin, Python, C++, Go, and Rust. Use it for same-language traffic because it follows the runtime's native type system, supports a broader language-specific object surface, and is optimized for that runtime. -### Java Serialization +Xlang/default usage uses schema-compatible mode by default. Native mode uses schema-consistent payloads by default unless compatible mode is enabled explicitly. -When you do not need cross-language support, use Java mode for optimal performance. +## Xlang Mode -This example creates a reusable Java-mode runtime, registers a user class, and then performs a basic serialize/deserialize round trip. In production code, keep the `Fory` instance alive and reuse it across requests instead of rebuilding it for every object. +Use xlang mode when bytes need to cross runtime boundaries. Register custom types with the same numeric ID or namespace/type name on every peer. + +Dual-mode runtimes set the xlang option explicitly in the examples below. Dart, JavaScript/TypeScript, C#, and Swift are xlang-only, so their examples do not show an xlang switch. + +### Java ```java -import org.apache.fory.*; -import org.apache.fory.config.*; +import org.apache.fory.Fory; -public class Example { - public static class Person { - String name; - int age; - } +public class XlangExample { + public record Person(String name, int age) {} public static void main(String[] args) { - // Create a Fory instance once and reuse it. - BaseFory fory = Fory.builder() - .withLanguage(Language.JAVA) - .requireClassRegistration(true) - // Replace `build` with `buildThreadSafeFory` for thread-safe usage. - .build(); - fory.register(Person.class); - - Person person = new Person(); - person.name = "chaokunyang"; - person.age = 28; + Fory fory = Fory.builder() + .withXlang(true) + .build(); + fory.register(Person.class, "example", "Person"); + Person person = new Person("chaokunyang", 28); byte[] bytes = fory.serialize(person); Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name + " " + result.age); + System.out.println(result.name() + " " + result.age()); } } ``` -For detailed Java usage including compatibility modes, compression, and advanced features, see [Java Serialization Guide](../guide/java/index.md). - -### Python Serialization - -Python native mode provides a high-performance drop-in replacement for `pickle` and `cloudpickle`. - -The example below uses a dataclass with explicit integer typing so Fory can preserve the intended schema efficiently. As with other runtimes, create the `Fory` instance once, register your types once, and then reuse it for repeated serialization. +### Python ```python from dataclasses import dataclass @@ -63,10 +52,10 @@ import pyfory @dataclass class Person: name: str - age: pyfory.int32 + age: pyfory.Int32 -fory = pyfory.Fory() -fory.register_type(Person) +fory = pyfory.Fory(xlang=True) +fory.register(Person, typename="example.Person") person = Person(name="chaokunyang", age=28) data = fory.serialize(person) @@ -74,13 +63,43 @@ result = fory.deserialize(data) print(result.name, result.age) ``` -For detailed Python usage including type hints, compatibility modes, and advanced features, see [Python Guide](../guide/python/index.md). +### Dart + +```dart +import 'package:fory/fory.dart'; -### Go Serialization +part 'person.fory.dart'; -Go native mode is the default. Register your structs once, then reuse the same `Fory` instance. +@ForyStruct() +class Person { + Person(); -The Go runtime works naturally with exported struct fields and explicit type registration. This snippet shows the standard flow: create `Fory`, register a struct type, serialize a value, and deserialize into a destination struct. + String name = ''; + + @ForyField(type: Int32Type()) + int age = 0; +} + +void main() { + final fory = Fory(); + PersonFory.register( + fory, + Person, + namespace: 'example', + typeName: 'Person', + ); + + final person = Person() + ..name = 'chaokunyang' + ..age = 28; + + final bytes = fory.serialize(person); + final result = fory.deserialize<Person>(bytes); + print('${result.name} ${result.age}'); +} +``` + +### Go ```go package main @@ -97,7 +116,7 @@ type Person struct { } func main() { - f := fory.New() + f := fory.New(fory.WithXlang(true)) if err := f.RegisterStruct(Person{}, 1); err != nil { panic(err) } @@ -117,66 +136,7 @@ func main() { } ``` -For detailed Go usage including configuration, struct tags, and schema evolution, see [Go Guide](../guide/go/index.md). - -### C# Serialization - -C# native serialization uses the `Apache.Fory` runtime together with `[ForyObject]` model types. - -In C#, the usual pattern is to mark your model with `[ForyObject]`, build a runtime once, and register the type before use. The example demonstrates the strongly typed `Serialize` and `Deserialize<T>` APIs that fit normal .NET application code. - -```csharp -using Apache.Fory; - -[ForyObject] -public sealed class Person -{ - public string Name { get; set; } = string.Empty; - public int Age { get; set; } -} - -Fory fory = Fory.Builder().Build(); -fory.Register<Person>(1); - -Person person = new() { Name = "chaokunyang", Age = 28 }; -byte[] data = fory.Serialize(person); -Person result = fory.Deserialize<Person>(data); - -Console.WriteLine($"{result.Name} {result.Age}"); -``` - -For detailed C# usage including source generators, references, and schema evolution, see [C# Guide](../guide/csharp/index.md). - -### Swift Serialization - -Swift native serialization uses `@ForyObject` models and the `Fory` runtime directly. - -Swift uses macro-based model definitions, so the example starts by annotating the type with `@ForyObject`, then registers the type ID and performs a typed round trip. This is the recommended starting point for app-side Swift usage. - -```swift -import Fory - -@ForyObject -struct Person: Equatable { - var name: String = "" - var age: Int32 = 0 -} - -let fory = Fory() -fory.register(Person.self, id: 1) - -let person = Person(name: "chaokunyang", age: 28) -let data = try fory.serialize(person) -let result: Person = try fory.deserialize(data) - -print("\(result.name) \(result.age)") -``` - -For detailed Swift usage including polymorphism, schema evolution, and troubleshooting, see [Swift Guide](../guide/swift/). - -### Rust Serialization - -Rust native mode uses `Fory::default()` and derive macros for compile-time type-safe serialization. The normal pattern is to derive `ForyObject`, register the type once, and then reuse the configured runtime for repeated serialization. +### Rust ```rust use fory::{Error, Fory, ForyObject}; @@ -188,8 +148,8 @@ struct Person { } fn main() -> Result<(), Error> { - let mut fory = Fory::default(); - fory.register::<Person>(1)?; + let mut fory = Fory::builder().xlang(true).build(); + fory.register_by_name::<Person>("example", "Person")?; let person = Person { name: "chaokunyang".to_string(), @@ -203,13 +163,12 @@ fn main() -> Result<(), Error> { } ``` -For detailed Rust usage including references, polymorphism, and row format support, see [Rust Guide](../guide/rust/index.md). - -### C++ Serialization - -C++ native mode uses the `FORY_STRUCT` macro to describe serializable fields and a configured `Fory` runtime to encode and decode values. For single-language C++ usage, set `xlang(false)` explicitly so the runtime stays in native mode. +### C++ ```cpp +#include <cassert> +#include <string> + #include "fory/serialization/fory.h" using namespace fory::serialization; @@ -226,41 +185,30 @@ struct Person { }; int main() { - auto fory = Fory::builder().xlang(false).build(); + auto fory = Fory::builder().xlang(true).build(); fory.register_struct<Person>(1); Person person{"chaokunyang", 28}; - - auto bytes = fory.serialize(person); - auto result = fory.deserialize<Person>(bytes.value()); - assert(result.ok()); - assert(person == result.value()); + auto bytes = fory.serialize(person).value(); + auto result = fory.deserialize<Person>(bytes).value(); + assert(person == result); return 0; } ``` -For detailed C++ usage including `FORY_STRUCT`, thread safety, and schema evolution, see [C++ Guide](../guide/cpp/index.md). - -### Scala Serialization - -Scala native mode provides optimized serialization for Scala-specific types including case classes, collections, and `Option`. - -For Scala projects, register the Scala serializers first so Fory understands Scala-specific data structures correctly. After that, you can register your case classes and use the same core API as the Java runtime. +### Scala ```scala import org.apache.fory.Fory -import org.apache.fory.config.Language -import org.apache.fory.serializer.scala.ScalaSerializers +import org.apache.fory.scala.ForyScala case class Person(name: String, age: Int) object Example { def main(args: Array[String]): Unit = { - val fory = Fory.builder() - .withLanguage(Language.JAVA) - .requireClassRegistration(true) + val fory: Fory = ForyScala.builder() + .withXlang(true) .build() - ScalaSerializers.registerSerializers(fory) fory.register(classOf[Person]) val bytes = fory.serialize(Person("chaokunyang", 28)) @@ -270,27 +218,19 @@ object Example { } ``` -For detailed Scala usage including collection serialization and integration patterns, see [Scala Guide](../guide/scala/index.md). - -### Kotlin Serialization - -Kotlin native mode provides optimized serialization for Kotlin-specific types including data classes, nullable types, and Kotlin collections. - -Kotlin follows the same builder flow as Java, with an extra registration step for Kotlin-specific serializers. The example uses a data class and shows the minimal setup needed for efficient native serialization. +### Kotlin ```kotlin -import org.apache.fory.Fory -import org.apache.fory.config.Language -import org.apache.fory.serializer.kotlin.KotlinSerializers +import org.apache.fory.ThreadSafeFory +import org.apache.fory.kotlin.ForyKotlin data class Person(val name: String, val age: Int) fun main() { - val fory = Fory.builder() - .withLanguage(Language.JAVA) + val fory: ThreadSafeFory = ForyKotlin.builder() + .withXlang(true) .requireClassRegistration(true) - .build() - KotlinSerializers.registerSerializers(fory) + .buildThreadSafeFory() fory.register(Person::class.java) val bytes = fory.serialize(Person("chaokunyang", 28)) @@ -299,133 +239,21 @@ fun main() { } ``` -For detailed Kotlin usage including null safety and default value support, see [kotlin/README.md](https://github.com/apache/fory/blob/main/kotlin/README.md). - -## Cross-Language Serialization +### JavaScript / TypeScript -**Only use xlang mode when you need cross-language data exchange.** Xlang mode adds type metadata overhead for cross-language compatibility and only supports types that can be mapped across all languages. - -The examples below use the same `Person` schema across multiple runtimes. In every language, enable xlang mode and register the type with the same ID or the same fully qualified name. - -### Java - -Java xlang usage is the baseline pattern for JVM services. Enable `Language.XLANG`, register the type with a stable ID or name, and make sure every peer language uses the same mapping. - -```java -import org.apache.fory.*; -import org.apache.fory.config.*; - -public class XlangExample { - public record Person(String name, int age) {} - - public static void main(String[] args) { - Fory fory = Fory.builder() - .withLanguage(Language.XLANG) - .build(); - - fory.register(Person.class, 1); - // fory.register(Person.class, "example.Person"); - - Person person = new Person("chaokunyang", 28); - byte[] bytes = fory.serialize(person); - Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name() + " " + result.age()); - } -} -``` - -### Go - -Go xlang mode is enabled through `WithXlang(true)`. The important part is not the Go syntax itself, but keeping the registered type identity aligned with every other language that reads or writes the payload. - -```go -package main - -import ( - "fmt" - - "github.com/apache/fory/go/fory" -) - -type Person struct { - Name string - Age int32 -} - -func main() { - f := fory.New(fory.WithXlang(true)) - if err := f.RegisterStruct(Person{}, 1); err != nil { - panic(err) - } - - person := &Person{Name: "chaokunyang", Age: 28} - data, err := f.Serialize(person) - if err != nil { - panic(err) - } - - var result Person - if err := f.Deserialize(data, &result); err != nil { - panic(err) - } - - fmt.Printf("%s %d\n", result.Name, result.Age) -} -``` - -### Rust - -Rust follows the same cross-language contract, but expresses it through derived traits and explicit registration on the `Fory` instance. Once the type ID matches the other runtimes, the payload can move across language boundaries safely. - -```rust -use fory::{Fory, ForyObject}; -use std::error::Error; - -#[derive(ForyObject, Debug)] -struct Person { - name: String, - age: i32, -} - -fn main() -> Result<(), Box<dyn Error>> { - let mut fory = Fory::default().xlang(true); - fory.register::<Person>(1)?; - // fory.register_by_name::<Person>("example.Person")?; - - let person = Person { - name: "chaokunyang".to_string(), - age: 28, - }; - let bytes = fory.serialize(&person); - let result: Person = fory.deserialize(&bytes)?; - println!("{} {}", result.name, result.age); - Ok(()) -} -``` - -### JavaScript - -JavaScript cross-language support is schema-driven. Instead of registering a class, you describe the payload shape with `Type.object(...)`, then use the returned serializer pair to encode and decode values. - -These packages are not published to npm yet. Build them from the Apache Fory repository first, then use the following API shape. - -```javascript +```typescript import Fory, { Type } from "@apache-fory/core"; -/** - * `@apache-fory/hps` uses V8 fast calls directly from JIT. - * Use Node.js 20+ when enabling it. - * If installation fails, replace it with `const hps = null;`. - */ -import hps from "@apache-fory/hps"; - -const description = Type.object("example.Person", { - name: Type.string(), - age: Type.int32(), -}); +const personType = Type.struct( + { typeName: "example.Person" }, + { + name: Type.string(), + age: Type.int32(), + }, +); -const fory = new Fory({ hps }); -const { serialize, deserialize } = fory.registerSerializer(description); +const fory = new Fory(); +const { serialize, deserialize } = fory.register(personType); const payload = serialize({ name: "chaokunyang", age: 28 }); const result = deserialize(payload); @@ -434,8 +262,6 @@ console.log(result); ### C\# -C# cross-language code looks similar to native usage, but the runtime is explicitly configured for xlang and compatible mode. Use the same type ID or namespace/name mapping as your Java, Go, Swift, or Rust peers. - ```csharp using Apache.Fory; @@ -446,34 +272,28 @@ public sealed class Person public int Age { get; set; } } -Fory fory = Fory.Builder() - .Xlang(true) - .Compatible(true) - .Build(); - +Fory fory = Fory.Builder().Build(); fory.Register<Person>(1); Person person = new() { Name = "chaokunyang", Age = 28 }; -byte[] payload = fory.Serialize(person); -Person result = fory.Deserialize<Person>(payload); +byte[] data = fory.Serialize(person); +Person result = fory.Deserialize<Person>(data); Console.WriteLine($"{result.Name} {result.Age}"); ``` ### Swift -Swift cross-language serialization uses the same `@ForyObject` model style as native mode, but you create the runtime with `xlang: true`. Stable registration IDs are still the key requirement for interoperability. - ```swift import Fory -@ForyObject +@ForyStruct struct Person: Equatable { var name: String = "" var age: Int32 = 0 } -let fory = Fory(xlang: true, trackRef: false, compatible: true) +let fory = Fory() fory.register(Person.self, id: 1) let person = Person(name: "chaokunyang", age: 28) @@ -483,20 +303,91 @@ let result: Person = try fory.deserialize(data) print("\(result.name) \(result.age)") ``` -### Key Points +For more cross-language rules and examples, see: -- Enable xlang mode in every runtime (`Language.XLANG`, `WithXlang(true)`, `Xlang(true)`, `Fory(xlang: true, ...)`, and so on). -- Register types with **consistent IDs or names** across all languages. -- ID-based registration is more compact and faster, but it requires coordination to avoid conflicts. -- Name-based registration is easier to manage across teams, but it produces slightly larger payloads. -- Only use types that have cross-language mappings; see [Type Mapping](../specification/xlang_type_mapping.md). +- [Cross-Language Serialization Guide](../guide/xlang/index.md) +- [Java Guide](../guide/java/index.md) +- [Python Guide](../guide/python/index.md) +- [Dart Guide](../guide/dart/index.md) +- [Go Guide](../guide/go/index.md) +- [Rust Guide](../guide/rust/index.md) +- [C++ Guide](../guide/cpp/index.md) +- [C# Guide](../guide/csharp/index.md) +- [Swift Guide](../guide/swift/index.md) -For examples with circular references, shared references, and polymorphism across languages, see: +## Native Mode -- [Cross-Language Serialization Guide](../guide/xlang/index.md) -- [Go Guide - Cross Language](../guide/go/cross-language.md) -- [C# Guide - Cross Language](../guide/csharp/cross-language.md) -- [Swift Guide - Cross Language](../guide/swift/cross_language) +Use native mode only when every reader and writer is the same runtime family. Native mode supports broader language-specific object models than portable xlang mappings and is optimized for the owning runtime. + +Java and Python native modes are first-class same-language entry points. Use Java native mode when replacing JDK serialization, Kryo, FST, Hessian, or Java-only Protocol Buffers payloads. Use Python native mode when replacing `pickle` or `cloudpickle` for Python-only payloads. + +Dart, JavaScript/TypeScript, C#, and Swift do not expose native mode. + +### Java + +```java +Fory fory = Fory.builder() + .withXlang(false) + .requireClassRegistration(true) + .build(); +``` + +Register Java classes and use `serialize` / `deserialize` as usual. See the [Java Guide](../guide/java/index.md) for Java object hooks, `Externalizable`, dynamic object graphs, object copy, and Java native-mode zero-copy buffers. + +### Python + +```python +import pyfory + +fory = pyfory.Fory(xlang=False, ref=False, strict=True) +``` + +Register Python classes and use `serialize` / `deserialize` as usual. See the [Python Guide](../guide/python/index.md) for native-mode pickle replacement behavior and security settings. + +### Go + +```go +f := fory.New(fory.WithXlang(false)) +``` + +Use native mode for Go-only structs, pointers, interfaces, and Go-specific type behavior. See the [Go Guide](../guide/go/index.md) for struct tags and native-mode configuration. + +### Rust + +```rust +let mut fory = Fory::builder().xlang(false).build(); +``` + +Use native mode for Rust-only payloads that rely on Rust-specific object behavior. See the [Rust Guide](../guide/rust/index.md) for derive, references, and supported types. + +### C++ + +```cpp +auto fory = Fory::builder().xlang(false).build(); +``` + +Use native mode for C++-only traffic that does not need portable xlang type mappings. See the [C++ Guide](../guide/cpp/index.md) for `FORY_STRUCT`, configuration, and schema metadata. + +### Scala + +```scala +val fory = ForyScala.builder() + .withXlang(false) + .build() +``` + +Use native mode for Scala/JVM-only traffic that needs Scala case classes, collections, tuples, options, or enums on the JVM runtime path. See the [Scala Guide](../guide/scala/index.md). + +### Kotlin + +```kotlin +val fory = ForyKotlin.builder() + .withXlang(false) + .requireClassRegistration(true) + .buildThreadSafeFory() +``` + +Use native mode for Kotlin/JVM-only traffic that needs Kotlin data classes, nullable types, ranges, unsigned values, or Kotlin collections on the JVM runtime path. See the [Kotlin Guide](../guide/kotlin/index.md). ## Row Format Encoding diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/introduction/benchmark.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/introduction/benchmark.md index 5103ac2b4a..570d8f694d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/introduction/benchmark.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/introduction/benchmark.md @@ -85,7 +85,7 @@ Fory Swift 在标量对象和列表两类工作负载下,相比 Protobuf 和 M Fory JavaScript 在具有代表性的 Node.js 工作负载下,相比 Protocol Buffers 与 JSON 展现出较强性能表现。 -<img src="../benchmarks/javascript/throughput.png" width="90%"/> + 注意:结果取决于硬件、数据集和运行时版本。详细信息请参见 [JavaScript 性能测试报告](../benchmarks/javascript/README.md)。 @@ -93,6 +93,6 @@ Fory JavaScript 在具有代表性的 Node.js 工作负载下,相比 Protocol Fory Dart 在具有代表性的对象和列表工作负载下,相比 Protocol Buffers 展现出较强性能表现。 -<img src="../benchmarks/dart/throughput.png" width="90%"/> + 注意:结果取决于硬件、数据集和运行时版本。详细信息请参见 [Dart 性能测试报告](../benchmarks/dart/README.md)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md index 2a6c85cedf..f7839c6300 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md @@ -134,6 +134,24 @@ cd packages/hps npm run build ``` +## Dart + +在 `pubspec.yaml` 中添加 Apache Fory™ Dart: + +```yaml +dependencies: + fory: ^0.17.0 + +dev_dependencies: + build_runner: ^2.4.13 +``` + +定义带注解的类型后生成序列化器: + +```bash +dart run build_runner build --delete-conflicting-outputs +``` + ## C\# 安装 `Apache.Fory` NuGet 包。它同时包含运行时以及 `[ForyObject]` 类型所需的源代码生成器。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/usage.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/usage.md index 4799bf8096..c9bddb1775 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/usage.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/usage.md @@ -6,55 +6,44 @@ sidebar_position: 1 本章节提供 Apache Fory™ 的快速入门示例。 -## 原生序列化 +## 选择模式 -**当你只在单一语言内使用时,请始终选择原生模式。** 原生模式不需要为跨语言兼容写入额外类型元信息,因此性能最好。 +Apache Fory™ 有两种线格式模式: -xlang 模式会引入额外的元信息编码开销,并且只允许序列化所有受支持语言都能映射的类型。语言特有类型在 xlang 模式下会被拒绝。 +- **xlang 模式**是默认模式,也是跨语言共享载荷时使用的可移植格式。跨语言服务应使用 xlang 模式;Dart、JavaScript/TypeScript、C# 和 Swift 也只暴露 xlang 模式。 +- **原生模式**通过 `xlang=false` 或对应的 builder 选项启用,适用于 Java、Scala、Kotlin、Python、C++、Go 和 Rust。只有读写双方都属于同一运行时家族时才使用原生模式,因为它遵循该运行时的原生类型系统,支持更广的语言特有对象面,并针对该运行时优化。 -### Java 序列化 +xlang/default 用法默认使用 schema-compatible 模式。原生模式默认使用 schema-consistent 载荷,只有显式启用 compatible 模式时才改变。 -如果不需要跨语言支持,请使用 Java 模式以获得最佳性能。 +## xlang 模式 -下面的示例展示了最基本的 Java 原生用法:创建可复用的 Java 模式运行时、注册用户类型,然后完成一次序列化和反序列化往返。实际项目中不要为每个对象重新创建 `Fory` 实例,而应长期复用同一个实例。 +当字节需要跨运行时边界传输时使用 xlang 模式。自定义类型需要在每个对端使用相同的数字 ID 或 namespace/type name 注册。 + +下面的示例中,支持双模式的运行时都会显式设置 xlang 选项。Dart、JavaScript/TypeScript、C# 和 Swift 只支持 xlang 模式,因此示例不会展示 xlang 开关。 + +### Java ```java -import org.apache.fory.*; -import org.apache.fory.config.*; +import org.apache.fory.Fory; -public class Example { - public static class Person { - String name; - int age; - } +public class XlangExample { + public record Person(String name, int age) {} public static void main(String[] args) { - // 创建一次 Fory 实例并重复复用。 - BaseFory fory = Fory.builder() - .withLanguage(Language.JAVA) - .requireClassRegistration(true) - // 如果需要线程安全用法,请将 `build` 替换为 `buildThreadSafeFory`。 - .build(); - fory.register(Person.class); - - Person person = new Person(); - person.name = "chaokunyang"; - person.age = 28; + Fory fory = Fory.builder() + .withXlang(true) + .build(); + fory.register(Person.class, "example", "Person"); + Person person = new Person("chaokunyang", 28); byte[] bytes = fory.serialize(person); Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name + " " + result.age); + System.out.println(result.name() + " " + result.age()); } } ``` -关于兼容模式、压缩和更多高级特性,请参见 [Java 序列化指南](../guide/java/index.md)。 - -### Python 序列化 - -Python 原生模式可以作为 `pickle` 和 `cloudpickle` 的高性能替代方案。 - -这个示例使用 dataclass 和显式整数类型注解,让 Fory 能以更清晰的 Schema 进行高效序列化。与其他语言一样,推荐只创建一次 `Fory` 实例、只注册一次类型,然后在后续调用中重复使用。 +### Python ```python from dataclasses import dataclass @@ -63,10 +52,10 @@ import pyfory @dataclass class Person: name: str - age: pyfory.int32 + age: pyfory.Int32 -fory = pyfory.Fory() -fory.register_type(Person) +fory = pyfory.Fory(xlang=True) +fory.register(Person, typename="example.Person") person = Person(name="chaokunyang", age=28) data = fory.serialize(person) @@ -74,13 +63,43 @@ result = fory.deserialize(data) print(result.name, result.age) ``` -关于类型注解、兼容模式和更多高级特性,请参见 [Python 指南](../guide/python/index.md)。 +### Dart + +```dart +import 'package:fory/fory.dart'; -### Go 序列化 +part 'person.fory.dart'; -Go 原生模式默认启用。注册一次结构体后,重复复用同一个 `Fory` 实例即可。 +@ForyStruct() +class Person { + Person(); -Go 运行时天然适配导出的 struct 字段和显式类型注册。下面的代码演示了最常见的流程:创建 `Fory`、注册结构体类型、序列化一个值,再反序列化到目标结构体中。 + String name = ''; + + @ForyField(type: Int32Type()) + int age = 0; +} + +void main() { + final fory = Fory(); + PersonFory.register( + fory, + Person, + namespace: 'example', + typeName: 'Person', + ); + + final person = Person() + ..name = 'chaokunyang' + ..age = 28; + + final bytes = fory.serialize(person); + final result = fory.deserialize<Person>(bytes); + print('${result.name} ${result.age}'); +} +``` + +### Go ```go package main @@ -97,7 +116,7 @@ type Person struct { } func main() { - f := fory.New() + f := fory.New(fory.WithXlang(true)) if err := f.RegisterStruct(Person{}, 1); err != nil { panic(err) } @@ -117,66 +136,7 @@ func main() { } ``` -关于配置、struct tag 和 Schema 演进,请参见 [Go 指南](../guide/go/index.md)。 - -### C# 序列化 - -C# 原生序列化使用 `Apache.Fory` 运行时和 `[ForyObject]` 模型类型。 - -在 C# 中,常见模式是先用 `[ForyObject]` 标记模型,再创建一次运行时并在使用前注册类型。示例展示的是强类型的 `Serialize` / `Deserialize<T>` API,这也是 .NET 应用中最直接的用法。 - -```csharp -using Apache.Fory; - -[ForyObject] -public sealed class Person -{ - public string Name { get; set; } = string.Empty; - public int Age { get; set; } -} - -Fory fory = Fory.Builder().Build(); -fory.Register<Person>(1); - -Person person = new() { Name = "chaokunyang", Age = 28 }; -byte[] data = fory.Serialize(person); -Person result = fory.Deserialize<Person>(data); - -Console.WriteLine($"{result.Name} {result.Age}"); -``` - -关于源代码生成器、引用跟踪和 Schema 演进,请参见 [C# 指南](../guide/csharp/index.md)。 - -### Swift 序列化 - -Swift 原生序列化直接使用 `@ForyObject` 模型和 `Fory` 运行时。 - -Swift 通过宏定义模型类型,因此示例先使用 `@ForyObject` 标记类型,再注册类型 ID 并完成一次强类型往返。这是 Swift 应用侧最推荐的入门方式。 - -```swift -import Fory - -@ForyObject -struct Person: Equatable { - var name: String = "" - var age: Int32 = 0 -} - -let fory = Fory() -fory.register(Person.self, id: 1) - -let person = Person(name: "chaokunyang", age: 28) -let data = try fory.serialize(person) -let result: Person = try fory.deserialize(data) - -print("\(result.name) \(result.age)") -``` - -关于多态、Schema 演进和常见问题排查,请参见 [Swift 指南](../guide/swift/)。 - -### Rust 序列化 - -Rust 原生模式使用 `Fory::default()` 和 derive 宏来实现编译期类型安全的序列化。常见模式是先为类型派生 `ForyObject`,注册一次类型,再重复复用已经配置好的运行时。 +### Rust ```rust use fory::{Error, Fory, ForyObject}; @@ -188,8 +148,8 @@ struct Person { } fn main() -> Result<(), Error> { - let mut fory = Fory::default(); - fory.register::<Person>(1)?; + let mut fory = Fory::builder().xlang(true).build(); + fory.register_by_name::<Person>("example", "Person")?; let person = Person { name: "chaokunyang".to_string(), @@ -203,13 +163,12 @@ fn main() -> Result<(), Error> { } ``` -关于引用、多态和 row format 支持,请参见 [Rust 指南](../guide/rust/index.md)。 - -### C++ 序列化 - -C++ 原生模式使用 `FORY_STRUCT` 宏描述可序列化字段,再通过配置好的 `Fory` 运行时对值进行编码和解码。对于单语言 C++ 场景,建议显式设置 `xlang(false)`,让运行时保持在原生模式。 +### C++ ```cpp +#include <cassert> +#include <string> + #include "fory/serialization/fory.h" using namespace fory::serialization; @@ -226,41 +185,30 @@ struct Person { }; int main() { - auto fory = Fory::builder().xlang(false).build(); + auto fory = Fory::builder().xlang(true).build(); fory.register_struct<Person>(1); Person person{"chaokunyang", 28}; - - auto bytes = fory.serialize(person); - auto result = fory.deserialize<Person>(bytes.value()); - assert(result.ok()); - assert(person == result.value()); + auto bytes = fory.serialize(person).value(); + auto result = fory.deserialize<Person>(bytes).value(); + assert(person == result); return 0; } ``` -关于 `FORY_STRUCT`、线程安全和 Schema 演进,请参见 [C++ 指南](../guide/cpp/index.md)。 - -### Scala 序列化 - -Scala 原生模式对 case class、集合和 `Option` 等 Scala 特有类型提供了优化支持。 - -在 Scala 项目中,应先注册 Scala 专用序列化器,让 Fory 正确理解 Scala 特有的数据结构。完成这一步后,就可以像 Java 运行时一样注册 case class 并执行序列化。 +### Scala ```scala import org.apache.fory.Fory -import org.apache.fory.config.Language -import org.apache.fory.serializer.scala.ScalaSerializers +import org.apache.fory.scala.ForyScala case class Person(name: String, age: Int) object Example { def main(args: Array[String]): Unit = { - val fory = Fory.builder() - .withLanguage(Language.JAVA) - .requireClassRegistration(true) + val fory: Fory = ForyScala.builder() + .withXlang(true) .build() - ScalaSerializers.registerSerializers(fory) fory.register(classOf[Person]) val bytes = fory.serialize(Person("chaokunyang", 28)) @@ -270,27 +218,19 @@ object Example { } ``` -关于集合序列化和集成模式,请参见 [Scala 指南](../guide/scala/index.md)。 - -### Kotlin 序列化 - -Kotlin 原生模式对 data class、可空类型和 Kotlin 集合提供了优化支持。 - -Kotlin 的整体流程与 Java 类似,只是额外需要注册 Kotlin 专用序列化器。下面的示例使用 data class,展示了进行高效原生序列化所需的最小配置。 +### Kotlin ```kotlin -import org.apache.fory.Fory -import org.apache.fory.config.Language -import org.apache.fory.serializer.kotlin.KotlinSerializers +import org.apache.fory.ThreadSafeFory +import org.apache.fory.kotlin.ForyKotlin data class Person(val name: String, val age: Int) fun main() { - val fory = Fory.builder() - .withLanguage(Language.JAVA) + val fory: ThreadSafeFory = ForyKotlin.builder() + .withXlang(true) .requireClassRegistration(true) - .build() - KotlinSerializers.registerSerializers(fory) + .buildThreadSafeFory() fory.register(Person::class.java) val bytes = fory.serialize(Person("chaokunyang", 28)) @@ -299,133 +239,21 @@ fun main() { } ``` -关于空安全和默认值支持,请参见 [kotlin/README.md](https://github.com/apache/fory/blob/main/kotlin/README.md)。 - -## 跨语言序列化 +### JavaScript / TypeScript -**只有在确实需要跨语言数据交换时才使用 xlang 模式。** xlang 模式会为跨语言兼容增加类型元信息开销,并且只支持能够在所有语言之间映射的类型。 - -下面的示例在多个运行时中使用同一个 `Person` Schema。无论使用哪种语言,都需要启用 xlang 模式,并用相同的 ID 或相同的全限定名称注册类型。 - -### Java - -Java 的 xlang 用法可以看作 JVM 服务中的基准模式。启用 `Language.XLANG` 后,用稳定的 ID 或名称注册类型,并确保所有对端语言都使用相同的映射关系。 - -```java -import org.apache.fory.*; -import org.apache.fory.config.*; - -public class XlangExample { - public record Person(String name, int age) {} - - public static void main(String[] args) { - Fory fory = Fory.builder() - .withLanguage(Language.XLANG) - .build(); - - fory.register(Person.class, 1); - // fory.register(Person.class, "example.Person"); - - Person person = new Person("chaokunyang", 28); - byte[] bytes = fory.serialize(person); - Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name() + " " + result.age()); - } -} -``` - -### Go - -Go 通过 `WithXlang(true)` 启用跨语言模式。真正关键的不是 Go 语法本身,而是保证注册的类型身份与其他读写同一载荷的语言完全一致。 - -```go -package main - -import ( - "fmt" - - "github.com/apache/fory/go/fory" -) - -type Person struct { - Name string - Age int32 -} - -func main() { - f := fory.New(fory.WithXlang(true)) - if err := f.RegisterStruct(Person{}, 1); err != nil { - panic(err) - } - - person := &Person{Name: "chaokunyang", Age: 28} - data, err := f.Serialize(person) - if err != nil { - panic(err) - } - - var result Person - if err := f.Deserialize(data, &result); err != nil { - panic(err) - } - - fmt.Printf("%s %d\n", result.Name, result.Age) -} -``` - -### Rust - -Rust 同样遵循这套跨语言约定,只是通过派生 trait 和在 `Fory` 实例上显式注册来表达。只要类型 ID 与其他运行时一致,载荷就可以安全地跨语言流转。 - -```rust -use fory::{Fory, ForyObject}; -use std::error::Error; - -#[derive(ForyObject, Debug)] -struct Person { - name: String, - age: i32, -} - -fn main() -> Result<(), Box<dyn Error>> { - let mut fory = Fory::default().xlang(true); - fory.register::<Person>(1)?; - // fory.register_by_name::<Person>("example.Person")?; - - let person = Person { - name: "chaokunyang".to_string(), - age: 28, - }; - let bytes = fory.serialize(&person); - let result: Person = fory.deserialize(&bytes)?; - println!("{} {}", result.name, result.age); - Ok(()) -} -``` - -### JavaScript - -JavaScript 的跨语言支持是基于 Schema 描述的。它不是注册类,而是通过 `Type.object(...)` 描述载荷结构,再使用返回的序列化器来编码和解码数据。 - -这些包目前还没有发布到 npm。因此请先从 Apache Fory 仓库完成构建,再按下面的 API 方式使用。 - -```javascript +```typescript import Fory, { Type } from "@apache-fory/core"; -/** - * `@apache-fory/hps` 会通过 JIT 直接使用 V8 fast calls。 - * 启用时请使用 Node.js 20+。 - * 如果安装失败,请将它替换为 `const hps = null;`。 - */ -import hps from "@apache-fory/hps"; - -const description = Type.object("example.Person", { - name: Type.string(), - age: Type.int32(), -}); +const personType = Type.struct( + { typeName: "example.Person" }, + { + name: Type.string(), + age: Type.int32(), + }, +); -const fory = new Fory({ hps }); -const { serialize, deserialize } = fory.registerSerializer(description); +const fory = new Fory(); +const { serialize, deserialize } = fory.register(personType); const payload = serialize({ name: "chaokunyang", age: 28 }); const result = deserialize(payload); @@ -434,8 +262,6 @@ console.log(result); ### C\# -C# 的跨语言代码看起来与原生模式很接近,但运行时需要显式启用 xlang 和 compatible 模式。与此同时,仍然必须与 Java、Go、Swift、Rust 等对端使用相同的类型 ID 或 namespace/name 映射。 - ```csharp using Apache.Fory; @@ -446,34 +272,28 @@ public sealed class Person public int Age { get; set; } } -Fory fory = Fory.Builder() - .Xlang(true) - .Compatible(true) - .Build(); - +Fory fory = Fory.Builder().Build(); fory.Register<Person>(1); Person person = new() { Name = "chaokunyang", Age = 28 }; -byte[] payload = fory.Serialize(person); -Person result = fory.Deserialize<Person>(payload); +byte[] data = fory.Serialize(person); +Person result = fory.Deserialize<Person>(data); Console.WriteLine($"{result.Name} {result.Age}"); ``` ### Swift -Swift 的跨语言序列化仍然使用与原生模式相同的 `@ForyObject` 模型风格,只是在创建运行时时要传入 `xlang: true`。要实现互操作,稳定的注册 ID 仍然是最核心的要求。 - ```swift import Fory -@ForyObject +@ForyStruct struct Person: Equatable { var name: String = "" var age: Int32 = 0 } -let fory = Fory(xlang: true, trackRef: false, compatible: true) +let fory = Fory() fory.register(Person.self, id: 1) let person = Person(name: "chaokunyang", age: 28) @@ -483,24 +303,95 @@ let result: Person = try fory.deserialize(data) print("\(result.name) \(result.age)") ``` -### 要点 +更多跨语言规则和示例请参见: -- 在每个运行时中都显式启用 xlang 模式,例如 `Language.XLANG`、`WithXlang(true)`、`Xlang(true)`、`Fory(xlang: true, ...)` 等。 -- 在所有语言中使用**一致的 ID 或名称**注册类型。 -- 基于 ID 的注册更紧凑、速度更快,但需要集中协调以避免冲突。 -- 基于名称的注册更容易在团队间管理,但载荷会稍大一些。 -- 只使用具备跨语言映射的类型,详见 [Type Mapping](../specification/xlang_type_mapping.md)。 +- [跨语言序列化指南](../guide/xlang/index.md) +- [Java 指南](../guide/java/index.md) +- [Python 指南](../guide/python/index.md) +- [Dart 指南](../guide/dart/index.md) +- [Go 指南](../guide/go/index.md) +- [Rust 指南](../guide/rust/index.md) +- [C++ 指南](../guide/cpp/index.md) +- [C# 指南](../guide/csharp/index.md) +- [Swift 指南](../guide/swift/index.md) -关于跨语言场景中的循环引用、共享引用和多态示例,请参见: +## 原生模式 -- [跨语言序列化指南](../guide/xlang/index.md) -- [Go 指南 - 跨语言](../guide/go/cross-language.md) -- [C# 指南 - 跨语言](../guide/csharp/cross-language.md) -- [Swift 指南 - 跨语言](../guide/swift/cross_language) +只有在每个读写方都属于同一运行时家族时才使用原生模式。原生模式支持比可移植 xlang 映射更广的语言特有对象模型,并针对所属运行时优化。 + +Java 和 Python 的原生模式都是同语言场景的一等入口。当你要替换 JDK serialization、Kryo、FST、Hessian 或 Java-only Protocol Buffers 载荷时,Java 应从原生模式开始。当你要替换 `pickle` 或 `cloudpickle` 并且载荷只在 Python 内流转时,Python 应使用原生模式。 + +Dart、JavaScript/TypeScript、C# 和 Swift 不暴露原生模式。 + +### Java + +```java +Fory fory = Fory.builder() + .withXlang(false) + .requireClassRegistration(true) + .build(); +``` + +注册 Java 类后照常使用 `serialize` / `deserialize`。Java 对象钩子、`Externalizable`、动态对象图、对象拷贝和 Java 原生模式 zero-copy buffer 参见 [Java 指南](../guide/java/index.md)。 + +### Python + +```python +import pyfory + +fory = pyfory.Fory(xlang=False, ref=False, strict=True) +``` + +注册 Python 类后照常使用 `serialize` / `deserialize`。原生模式的 pickle 替代行为和安全设置参见 [Python 指南](../guide/python/index.md)。 + +### Go + +```go +f := fory.New(fory.WithXlang(false)) +``` + +Go-only 结构体、指针、接口和 Go 特有类型行为可使用原生模式。struct tag 和原生模式配置参见 [Go 指南](../guide/go/index.md)。 + +### Rust + +```rust +let mut fory = Fory::builder().xlang(false).build(); +``` + +依赖 Rust 特有对象行为的 Rust-only 载荷可使用原生模式。derive、引用和支持类型参见 [Rust 指南](../guide/rust/index.md)。 + +### C++ + +```cpp +auto fory = Fory::builder().xlang(false).build(); +``` + +不需要可移植 xlang 类型映射的 C++-only 流量可使用原生模式。`FORY_STRUCT`、配置和 schema metadata 参见 [C++ 指南](../guide/cpp/index.md)。 + +### Scala + +```scala +val fory = ForyScala.builder() + .withXlang(false) + .build() +``` + +需要 Scala case class、集合、tuple、option 或 enum 并且只在 Scala/JVM 内流转的载荷可使用原生模式。参见 [Scala 指南](../guide/scala/index.md)。 + +### Kotlin + +```kotlin +val fory = ForyKotlin.builder() + .withXlang(false) + .requireClassRegistration(true) + .buildThreadSafeFory() +``` + +需要 Kotlin data class、可空类型、range、unsigned value 或 Kotlin 集合并且只在 Kotlin/JVM 内流转的载荷可使用原生模式。参见 [Kotlin 指南](../guide/kotlin/index.md)。 ## Row Format 编码 -Row format 提供零拷贝随机访问能力,非常适合分析型负载和数据处理流水线。 +Row format 提供对序列化数据的零拷贝随机访问,适合分析型工作负载和数据处理管线。 ### Java @@ -536,19 +427,19 @@ for (int i = 0; i < 1000000; i++) { } foo.f4 = bars; -// 序列化为 row format(可被 Python 以零拷贝方式读取) +// Serialize to row format (can be zero-copy read by Python) BinaryRow binaryRow = encoder.toRow(foo); -// 反序列化整个对象 +// Deserialize entire object Foo newFoo = encoder.fromRow(binaryRow); -// 不做完整反序列化,直接零拷贝访问嵌套字段 -BinaryArray binaryArray2 = binaryRow.getArray(1); // 访问 f2 字段 -BinaryArray binaryArray4 = binaryRow.getArray(3); // 访问 f4 字段 -BinaryRow barStruct = binaryArray4.getStruct(10); // 访问第 11 个 Bar 元素 -long value = barStruct.getArray(1).getInt64(5); // 访问嵌套值 +// Zero-copy access to nested fields without full deserialization +BinaryArray binaryArray2 = binaryRow.getArray(1); // Access f2 field +BinaryArray binaryArray4 = binaryRow.getArray(3); // Access f4 field +BinaryRow barStruct = binaryArray4.getStruct(10); // Access 11th Bar element +long value = barStruct.getArray(1).getInt64(5); // Access nested value -// 部分反序列化 +// Partial deserialization RowEncoder<Bar> barEncoder = Encoders.bean(Bar.class); Bar newBar = barEncoder.fromRow(barStruct); Bar newBar2 = barEncoder.fromRow(binaryArray4.getStruct(20)); @@ -582,14 +473,14 @@ foo = Foo( f4=[Bar(f1=f"s{i}", f2=list(range(10))) for i in range(1000_000)] ) -# 序列化为 row format +# Serialize to row format binary: bytes = encoder.to_row(foo).to_bytes() -# 无需完整反序列化即可零拷贝随机访问 +# Zero-copy random access without full deserialization foo_row = pyfory.RowData(encoder.schema, binary) -print(foo_row.f2[100000]) # 直接访问元素 -print(foo_row.f4[100000].f1) # 访问嵌套字段 -print(foo_row.f4[200000].f2[5]) # 访问更深层的嵌套字段 +print(foo_row.f2[100000]) # Access element directly +print(foo_row.f4[100000].f1) # Access nested field +print(foo_row.f4[200000].f2[5]) # Access deeply nested field ``` 更多 row format 细节请参见 [Java Row Format 指南](../guide/java/row-format.md) 或 [Python Row Format 指南](../guide/python/row-format.md)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/introduction/benchmark.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/introduction/benchmark.md index 5103ac2b4a..570d8f694d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/introduction/benchmark.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/introduction/benchmark.md @@ -85,7 +85,7 @@ Fory Swift 在标量对象和列表两类工作负载下,相比 Protobuf 和 M Fory JavaScript 在具有代表性的 Node.js 工作负载下,相比 Protocol Buffers 与 JSON 展现出较强性能表现。 -<img src="../benchmarks/javascript/throughput.png" width="90%"/> + 注意:结果取决于硬件、数据集和运行时版本。详细信息请参见 [JavaScript 性能测试报告](../benchmarks/javascript/README.md)。 @@ -93,6 +93,6 @@ Fory JavaScript 在具有代表性的 Node.js 工作负载下,相比 Protocol Fory Dart 在具有代表性的对象和列表工作负载下,相比 Protocol Buffers 展现出较强性能表现。 -<img src="../benchmarks/dart/throughput.png" width="90%"/> + 注意:结果取决于硬件、数据集和运行时版本。详细信息请参见 [Dart 性能测试报告](../benchmarks/dart/README.md)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/start/install.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/start/install.md index 2a6c85cedf..f7839c6300 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/start/install.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/start/install.md @@ -134,6 +134,24 @@ cd packages/hps npm run build ``` +## Dart + +在 `pubspec.yaml` 中添加 Apache Fory™ Dart: + +```yaml +dependencies: + fory: ^0.17.0 + +dev_dependencies: + build_runner: ^2.4.13 +``` + +定义带注解的类型后生成序列化器: + +```bash +dart run build_runner build --delete-conflicting-outputs +``` + ## C\# 安装 `Apache.Fory` NuGet 包。它同时包含运行时以及 `[ForyObject]` 类型所需的源代码生成器。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/start/usage.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/start/usage.md index 4799bf8096..c9bddb1775 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/start/usage.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.17/start/usage.md @@ -6,55 +6,44 @@ sidebar_position: 1 本章节提供 Apache Fory™ 的快速入门示例。 -## 原生序列化 +## 选择模式 -**当你只在单一语言内使用时,请始终选择原生模式。** 原生模式不需要为跨语言兼容写入额外类型元信息,因此性能最好。 +Apache Fory™ 有两种线格式模式: -xlang 模式会引入额外的元信息编码开销,并且只允许序列化所有受支持语言都能映射的类型。语言特有类型在 xlang 模式下会被拒绝。 +- **xlang 模式**是默认模式,也是跨语言共享载荷时使用的可移植格式。跨语言服务应使用 xlang 模式;Dart、JavaScript/TypeScript、C# 和 Swift 也只暴露 xlang 模式。 +- **原生模式**通过 `xlang=false` 或对应的 builder 选项启用,适用于 Java、Scala、Kotlin、Python、C++、Go 和 Rust。只有读写双方都属于同一运行时家族时才使用原生模式,因为它遵循该运行时的原生类型系统,支持更广的语言特有对象面,并针对该运行时优化。 -### Java 序列化 +xlang/default 用法默认使用 schema-compatible 模式。原生模式默认使用 schema-consistent 载荷,只有显式启用 compatible 模式时才改变。 -如果不需要跨语言支持,请使用 Java 模式以获得最佳性能。 +## xlang 模式 -下面的示例展示了最基本的 Java 原生用法:创建可复用的 Java 模式运行时、注册用户类型,然后完成一次序列化和反序列化往返。实际项目中不要为每个对象重新创建 `Fory` 实例,而应长期复用同一个实例。 +当字节需要跨运行时边界传输时使用 xlang 模式。自定义类型需要在每个对端使用相同的数字 ID 或 namespace/type name 注册。 + +下面的示例中,支持双模式的运行时都会显式设置 xlang 选项。Dart、JavaScript/TypeScript、C# 和 Swift 只支持 xlang 模式,因此示例不会展示 xlang 开关。 + +### Java ```java -import org.apache.fory.*; -import org.apache.fory.config.*; +import org.apache.fory.Fory; -public class Example { - public static class Person { - String name; - int age; - } +public class XlangExample { + public record Person(String name, int age) {} public static void main(String[] args) { - // 创建一次 Fory 实例并重复复用。 - BaseFory fory = Fory.builder() - .withLanguage(Language.JAVA) - .requireClassRegistration(true) - // 如果需要线程安全用法,请将 `build` 替换为 `buildThreadSafeFory`。 - .build(); - fory.register(Person.class); - - Person person = new Person(); - person.name = "chaokunyang"; - person.age = 28; + Fory fory = Fory.builder() + .withXlang(true) + .build(); + fory.register(Person.class, "example", "Person"); + Person person = new Person("chaokunyang", 28); byte[] bytes = fory.serialize(person); Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name + " " + result.age); + System.out.println(result.name() + " " + result.age()); } } ``` -关于兼容模式、压缩和更多高级特性,请参见 [Java 序列化指南](../guide/java/index.md)。 - -### Python 序列化 - -Python 原生模式可以作为 `pickle` 和 `cloudpickle` 的高性能替代方案。 - -这个示例使用 dataclass 和显式整数类型注解,让 Fory 能以更清晰的 Schema 进行高效序列化。与其他语言一样,推荐只创建一次 `Fory` 实例、只注册一次类型,然后在后续调用中重复使用。 +### Python ```python from dataclasses import dataclass @@ -63,10 +52,10 @@ import pyfory @dataclass class Person: name: str - age: pyfory.int32 + age: pyfory.Int32 -fory = pyfory.Fory() -fory.register_type(Person) +fory = pyfory.Fory(xlang=True) +fory.register(Person, typename="example.Person") person = Person(name="chaokunyang", age=28) data = fory.serialize(person) @@ -74,13 +63,43 @@ result = fory.deserialize(data) print(result.name, result.age) ``` -关于类型注解、兼容模式和更多高级特性,请参见 [Python 指南](../guide/python/index.md)。 +### Dart + +```dart +import 'package:fory/fory.dart'; -### Go 序列化 +part 'person.fory.dart'; -Go 原生模式默认启用。注册一次结构体后,重复复用同一个 `Fory` 实例即可。 +@ForyStruct() +class Person { + Person(); -Go 运行时天然适配导出的 struct 字段和显式类型注册。下面的代码演示了最常见的流程:创建 `Fory`、注册结构体类型、序列化一个值,再反序列化到目标结构体中。 + String name = ''; + + @ForyField(type: Int32Type()) + int age = 0; +} + +void main() { + final fory = Fory(); + PersonFory.register( + fory, + Person, + namespace: 'example', + typeName: 'Person', + ); + + final person = Person() + ..name = 'chaokunyang' + ..age = 28; + + final bytes = fory.serialize(person); + final result = fory.deserialize<Person>(bytes); + print('${result.name} ${result.age}'); +} +``` + +### Go ```go package main @@ -97,7 +116,7 @@ type Person struct { } func main() { - f := fory.New() + f := fory.New(fory.WithXlang(true)) if err := f.RegisterStruct(Person{}, 1); err != nil { panic(err) } @@ -117,66 +136,7 @@ func main() { } ``` -关于配置、struct tag 和 Schema 演进,请参见 [Go 指南](../guide/go/index.md)。 - -### C# 序列化 - -C# 原生序列化使用 `Apache.Fory` 运行时和 `[ForyObject]` 模型类型。 - -在 C# 中,常见模式是先用 `[ForyObject]` 标记模型,再创建一次运行时并在使用前注册类型。示例展示的是强类型的 `Serialize` / `Deserialize<T>` API,这也是 .NET 应用中最直接的用法。 - -```csharp -using Apache.Fory; - -[ForyObject] -public sealed class Person -{ - public string Name { get; set; } = string.Empty; - public int Age { get; set; } -} - -Fory fory = Fory.Builder().Build(); -fory.Register<Person>(1); - -Person person = new() { Name = "chaokunyang", Age = 28 }; -byte[] data = fory.Serialize(person); -Person result = fory.Deserialize<Person>(data); - -Console.WriteLine($"{result.Name} {result.Age}"); -``` - -关于源代码生成器、引用跟踪和 Schema 演进,请参见 [C# 指南](../guide/csharp/index.md)。 - -### Swift 序列化 - -Swift 原生序列化直接使用 `@ForyObject` 模型和 `Fory` 运行时。 - -Swift 通过宏定义模型类型,因此示例先使用 `@ForyObject` 标记类型,再注册类型 ID 并完成一次强类型往返。这是 Swift 应用侧最推荐的入门方式。 - -```swift -import Fory - -@ForyObject -struct Person: Equatable { - var name: String = "" - var age: Int32 = 0 -} - -let fory = Fory() -fory.register(Person.self, id: 1) - -let person = Person(name: "chaokunyang", age: 28) -let data = try fory.serialize(person) -let result: Person = try fory.deserialize(data) - -print("\(result.name) \(result.age)") -``` - -关于多态、Schema 演进和常见问题排查,请参见 [Swift 指南](../guide/swift/)。 - -### Rust 序列化 - -Rust 原生模式使用 `Fory::default()` 和 derive 宏来实现编译期类型安全的序列化。常见模式是先为类型派生 `ForyObject`,注册一次类型,再重复复用已经配置好的运行时。 +### Rust ```rust use fory::{Error, Fory, ForyObject}; @@ -188,8 +148,8 @@ struct Person { } fn main() -> Result<(), Error> { - let mut fory = Fory::default(); - fory.register::<Person>(1)?; + let mut fory = Fory::builder().xlang(true).build(); + fory.register_by_name::<Person>("example", "Person")?; let person = Person { name: "chaokunyang".to_string(), @@ -203,13 +163,12 @@ fn main() -> Result<(), Error> { } ``` -关于引用、多态和 row format 支持,请参见 [Rust 指南](../guide/rust/index.md)。 - -### C++ 序列化 - -C++ 原生模式使用 `FORY_STRUCT` 宏描述可序列化字段,再通过配置好的 `Fory` 运行时对值进行编码和解码。对于单语言 C++ 场景,建议显式设置 `xlang(false)`,让运行时保持在原生模式。 +### C++ ```cpp +#include <cassert> +#include <string> + #include "fory/serialization/fory.h" using namespace fory::serialization; @@ -226,41 +185,30 @@ struct Person { }; int main() { - auto fory = Fory::builder().xlang(false).build(); + auto fory = Fory::builder().xlang(true).build(); fory.register_struct<Person>(1); Person person{"chaokunyang", 28}; - - auto bytes = fory.serialize(person); - auto result = fory.deserialize<Person>(bytes.value()); - assert(result.ok()); - assert(person == result.value()); + auto bytes = fory.serialize(person).value(); + auto result = fory.deserialize<Person>(bytes).value(); + assert(person == result); return 0; } ``` -关于 `FORY_STRUCT`、线程安全和 Schema 演进,请参见 [C++ 指南](../guide/cpp/index.md)。 - -### Scala 序列化 - -Scala 原生模式对 case class、集合和 `Option` 等 Scala 特有类型提供了优化支持。 - -在 Scala 项目中,应先注册 Scala 专用序列化器,让 Fory 正确理解 Scala 特有的数据结构。完成这一步后,就可以像 Java 运行时一样注册 case class 并执行序列化。 +### Scala ```scala import org.apache.fory.Fory -import org.apache.fory.config.Language -import org.apache.fory.serializer.scala.ScalaSerializers +import org.apache.fory.scala.ForyScala case class Person(name: String, age: Int) object Example { def main(args: Array[String]): Unit = { - val fory = Fory.builder() - .withLanguage(Language.JAVA) - .requireClassRegistration(true) + val fory: Fory = ForyScala.builder() + .withXlang(true) .build() - ScalaSerializers.registerSerializers(fory) fory.register(classOf[Person]) val bytes = fory.serialize(Person("chaokunyang", 28)) @@ -270,27 +218,19 @@ object Example { } ``` -关于集合序列化和集成模式,请参见 [Scala 指南](../guide/scala/index.md)。 - -### Kotlin 序列化 - -Kotlin 原生模式对 data class、可空类型和 Kotlin 集合提供了优化支持。 - -Kotlin 的整体流程与 Java 类似,只是额外需要注册 Kotlin 专用序列化器。下面的示例使用 data class,展示了进行高效原生序列化所需的最小配置。 +### Kotlin ```kotlin -import org.apache.fory.Fory -import org.apache.fory.config.Language -import org.apache.fory.serializer.kotlin.KotlinSerializers +import org.apache.fory.ThreadSafeFory +import org.apache.fory.kotlin.ForyKotlin data class Person(val name: String, val age: Int) fun main() { - val fory = Fory.builder() - .withLanguage(Language.JAVA) + val fory: ThreadSafeFory = ForyKotlin.builder() + .withXlang(true) .requireClassRegistration(true) - .build() - KotlinSerializers.registerSerializers(fory) + .buildThreadSafeFory() fory.register(Person::class.java) val bytes = fory.serialize(Person("chaokunyang", 28)) @@ -299,133 +239,21 @@ fun main() { } ``` -关于空安全和默认值支持,请参见 [kotlin/README.md](https://github.com/apache/fory/blob/main/kotlin/README.md)。 - -## 跨语言序列化 +### JavaScript / TypeScript -**只有在确实需要跨语言数据交换时才使用 xlang 模式。** xlang 模式会为跨语言兼容增加类型元信息开销,并且只支持能够在所有语言之间映射的类型。 - -下面的示例在多个运行时中使用同一个 `Person` Schema。无论使用哪种语言,都需要启用 xlang 模式,并用相同的 ID 或相同的全限定名称注册类型。 - -### Java - -Java 的 xlang 用法可以看作 JVM 服务中的基准模式。启用 `Language.XLANG` 后,用稳定的 ID 或名称注册类型,并确保所有对端语言都使用相同的映射关系。 - -```java -import org.apache.fory.*; -import org.apache.fory.config.*; - -public class XlangExample { - public record Person(String name, int age) {} - - public static void main(String[] args) { - Fory fory = Fory.builder() - .withLanguage(Language.XLANG) - .build(); - - fory.register(Person.class, 1); - // fory.register(Person.class, "example.Person"); - - Person person = new Person("chaokunyang", 28); - byte[] bytes = fory.serialize(person); - Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name() + " " + result.age()); - } -} -``` - -### Go - -Go 通过 `WithXlang(true)` 启用跨语言模式。真正关键的不是 Go 语法本身,而是保证注册的类型身份与其他读写同一载荷的语言完全一致。 - -```go -package main - -import ( - "fmt" - - "github.com/apache/fory/go/fory" -) - -type Person struct { - Name string - Age int32 -} - -func main() { - f := fory.New(fory.WithXlang(true)) - if err := f.RegisterStruct(Person{}, 1); err != nil { - panic(err) - } - - person := &Person{Name: "chaokunyang", Age: 28} - data, err := f.Serialize(person) - if err != nil { - panic(err) - } - - var result Person - if err := f.Deserialize(data, &result); err != nil { - panic(err) - } - - fmt.Printf("%s %d\n", result.Name, result.Age) -} -``` - -### Rust - -Rust 同样遵循这套跨语言约定,只是通过派生 trait 和在 `Fory` 实例上显式注册来表达。只要类型 ID 与其他运行时一致,载荷就可以安全地跨语言流转。 - -```rust -use fory::{Fory, ForyObject}; -use std::error::Error; - -#[derive(ForyObject, Debug)] -struct Person { - name: String, - age: i32, -} - -fn main() -> Result<(), Box<dyn Error>> { - let mut fory = Fory::default().xlang(true); - fory.register::<Person>(1)?; - // fory.register_by_name::<Person>("example.Person")?; - - let person = Person { - name: "chaokunyang".to_string(), - age: 28, - }; - let bytes = fory.serialize(&person); - let result: Person = fory.deserialize(&bytes)?; - println!("{} {}", result.name, result.age); - Ok(()) -} -``` - -### JavaScript - -JavaScript 的跨语言支持是基于 Schema 描述的。它不是注册类,而是通过 `Type.object(...)` 描述载荷结构,再使用返回的序列化器来编码和解码数据。 - -这些包目前还没有发布到 npm。因此请先从 Apache Fory 仓库完成构建,再按下面的 API 方式使用。 - -```javascript +```typescript import Fory, { Type } from "@apache-fory/core"; -/** - * `@apache-fory/hps` 会通过 JIT 直接使用 V8 fast calls。 - * 启用时请使用 Node.js 20+。 - * 如果安装失败,请将它替换为 `const hps = null;`。 - */ -import hps from "@apache-fory/hps"; - -const description = Type.object("example.Person", { - name: Type.string(), - age: Type.int32(), -}); +const personType = Type.struct( + { typeName: "example.Person" }, + { + name: Type.string(), + age: Type.int32(), + }, +); -const fory = new Fory({ hps }); -const { serialize, deserialize } = fory.registerSerializer(description); +const fory = new Fory(); +const { serialize, deserialize } = fory.register(personType); const payload = serialize({ name: "chaokunyang", age: 28 }); const result = deserialize(payload); @@ -434,8 +262,6 @@ console.log(result); ### C\# -C# 的跨语言代码看起来与原生模式很接近,但运行时需要显式启用 xlang 和 compatible 模式。与此同时,仍然必须与 Java、Go、Swift、Rust 等对端使用相同的类型 ID 或 namespace/name 映射。 - ```csharp using Apache.Fory; @@ -446,34 +272,28 @@ public sealed class Person public int Age { get; set; } } -Fory fory = Fory.Builder() - .Xlang(true) - .Compatible(true) - .Build(); - +Fory fory = Fory.Builder().Build(); fory.Register<Person>(1); Person person = new() { Name = "chaokunyang", Age = 28 }; -byte[] payload = fory.Serialize(person); -Person result = fory.Deserialize<Person>(payload); +byte[] data = fory.Serialize(person); +Person result = fory.Deserialize<Person>(data); Console.WriteLine($"{result.Name} {result.Age}"); ``` ### Swift -Swift 的跨语言序列化仍然使用与原生模式相同的 `@ForyObject` 模型风格,只是在创建运行时时要传入 `xlang: true`。要实现互操作,稳定的注册 ID 仍然是最核心的要求。 - ```swift import Fory -@ForyObject +@ForyStruct struct Person: Equatable { var name: String = "" var age: Int32 = 0 } -let fory = Fory(xlang: true, trackRef: false, compatible: true) +let fory = Fory() fory.register(Person.self, id: 1) let person = Person(name: "chaokunyang", age: 28) @@ -483,24 +303,95 @@ let result: Person = try fory.deserialize(data) print("\(result.name) \(result.age)") ``` -### 要点 +更多跨语言规则和示例请参见: -- 在每个运行时中都显式启用 xlang 模式,例如 `Language.XLANG`、`WithXlang(true)`、`Xlang(true)`、`Fory(xlang: true, ...)` 等。 -- 在所有语言中使用**一致的 ID 或名称**注册类型。 -- 基于 ID 的注册更紧凑、速度更快,但需要集中协调以避免冲突。 -- 基于名称的注册更容易在团队间管理,但载荷会稍大一些。 -- 只使用具备跨语言映射的类型,详见 [Type Mapping](../specification/xlang_type_mapping.md)。 +- [跨语言序列化指南](../guide/xlang/index.md) +- [Java 指南](../guide/java/index.md) +- [Python 指南](../guide/python/index.md) +- [Dart 指南](../guide/dart/index.md) +- [Go 指南](../guide/go/index.md) +- [Rust 指南](../guide/rust/index.md) +- [C++ 指南](../guide/cpp/index.md) +- [C# 指南](../guide/csharp/index.md) +- [Swift 指南](../guide/swift/index.md) -关于跨语言场景中的循环引用、共享引用和多态示例,请参见: +## 原生模式 -- [跨语言序列化指南](../guide/xlang/index.md) -- [Go 指南 - 跨语言](../guide/go/cross-language.md) -- [C# 指南 - 跨语言](../guide/csharp/cross-language.md) -- [Swift 指南 - 跨语言](../guide/swift/cross_language) +只有在每个读写方都属于同一运行时家族时才使用原生模式。原生模式支持比可移植 xlang 映射更广的语言特有对象模型,并针对所属运行时优化。 + +Java 和 Python 的原生模式都是同语言场景的一等入口。当你要替换 JDK serialization、Kryo、FST、Hessian 或 Java-only Protocol Buffers 载荷时,Java 应从原生模式开始。当你要替换 `pickle` 或 `cloudpickle` 并且载荷只在 Python 内流转时,Python 应使用原生模式。 + +Dart、JavaScript/TypeScript、C# 和 Swift 不暴露原生模式。 + +### Java + +```java +Fory fory = Fory.builder() + .withXlang(false) + .requireClassRegistration(true) + .build(); +``` + +注册 Java 类后照常使用 `serialize` / `deserialize`。Java 对象钩子、`Externalizable`、动态对象图、对象拷贝和 Java 原生模式 zero-copy buffer 参见 [Java 指南](../guide/java/index.md)。 + +### Python + +```python +import pyfory + +fory = pyfory.Fory(xlang=False, ref=False, strict=True) +``` + +注册 Python 类后照常使用 `serialize` / `deserialize`。原生模式的 pickle 替代行为和安全设置参见 [Python 指南](../guide/python/index.md)。 + +### Go + +```go +f := fory.New(fory.WithXlang(false)) +``` + +Go-only 结构体、指针、接口和 Go 特有类型行为可使用原生模式。struct tag 和原生模式配置参见 [Go 指南](../guide/go/index.md)。 + +### Rust + +```rust +let mut fory = Fory::builder().xlang(false).build(); +``` + +依赖 Rust 特有对象行为的 Rust-only 载荷可使用原生模式。derive、引用和支持类型参见 [Rust 指南](../guide/rust/index.md)。 + +### C++ + +```cpp +auto fory = Fory::builder().xlang(false).build(); +``` + +不需要可移植 xlang 类型映射的 C++-only 流量可使用原生模式。`FORY_STRUCT`、配置和 schema metadata 参见 [C++ 指南](../guide/cpp/index.md)。 + +### Scala + +```scala +val fory = ForyScala.builder() + .withXlang(false) + .build() +``` + +需要 Scala case class、集合、tuple、option 或 enum 并且只在 Scala/JVM 内流转的载荷可使用原生模式。参见 [Scala 指南](../guide/scala/index.md)。 + +### Kotlin + +```kotlin +val fory = ForyKotlin.builder() + .withXlang(false) + .requireClassRegistration(true) + .buildThreadSafeFory() +``` + +需要 Kotlin data class、可空类型、range、unsigned value 或 Kotlin 集合并且只在 Kotlin/JVM 内流转的载荷可使用原生模式。参见 [Kotlin 指南](../guide/kotlin/index.md)。 ## Row Format 编码 -Row format 提供零拷贝随机访问能力,非常适合分析型负载和数据处理流水线。 +Row format 提供对序列化数据的零拷贝随机访问,适合分析型工作负载和数据处理管线。 ### Java @@ -536,19 +427,19 @@ for (int i = 0; i < 1000000; i++) { } foo.f4 = bars; -// 序列化为 row format(可被 Python 以零拷贝方式读取) +// Serialize to row format (can be zero-copy read by Python) BinaryRow binaryRow = encoder.toRow(foo); -// 反序列化整个对象 +// Deserialize entire object Foo newFoo = encoder.fromRow(binaryRow); -// 不做完整反序列化,直接零拷贝访问嵌套字段 -BinaryArray binaryArray2 = binaryRow.getArray(1); // 访问 f2 字段 -BinaryArray binaryArray4 = binaryRow.getArray(3); // 访问 f4 字段 -BinaryRow barStruct = binaryArray4.getStruct(10); // 访问第 11 个 Bar 元素 -long value = barStruct.getArray(1).getInt64(5); // 访问嵌套值 +// Zero-copy access to nested fields without full deserialization +BinaryArray binaryArray2 = binaryRow.getArray(1); // Access f2 field +BinaryArray binaryArray4 = binaryRow.getArray(3); // Access f4 field +BinaryRow barStruct = binaryArray4.getStruct(10); // Access 11th Bar element +long value = barStruct.getArray(1).getInt64(5); // Access nested value -// 部分反序列化 +// Partial deserialization RowEncoder<Bar> barEncoder = Encoders.bean(Bar.class); Bar newBar = barEncoder.fromRow(barStruct); Bar newBar2 = barEncoder.fromRow(binaryArray4.getStruct(20)); @@ -582,14 +473,14 @@ foo = Foo( f4=[Bar(f1=f"s{i}", f2=list(range(10))) for i in range(1000_000)] ) -# 序列化为 row format +# Serialize to row format binary: bytes = encoder.to_row(foo).to_bytes() -# 无需完整反序列化即可零拷贝随机访问 +# Zero-copy random access without full deserialization foo_row = pyfory.RowData(encoder.schema, binary) -print(foo_row.f2[100000]) # 直接访问元素 -print(foo_row.f4[100000].f1) # 访问嵌套字段 -print(foo_row.f4[200000].f2[5]) # 访问更深层的嵌套字段 +print(foo_row.f2[100000]) # Access element directly +print(foo_row.f4[100000].f1) # Access nested field +print(foo_row.f4[200000].f2[5]) # Access deeply nested field ``` 更多 row format 细节请参见 [Java Row Format 指南](../guide/java/row-format.md) 或 [Python Row Format 指南](../guide/python/row-format.md)。 diff --git a/versioned_docs/version-0.17/introduction/benchmark.md b/versioned_docs/version-0.17/introduction/benchmark.md index eb97295449..515ae697aa 100644 --- a/versioned_docs/version-0.17/introduction/benchmark.md +++ b/versioned_docs/version-0.17/introduction/benchmark.md @@ -92,7 +92,7 @@ report for details: https://fory.apache.org/docs/benchmarks/swift/ Fory JavaScript demonstrates strong performance compared to Protocol Buffers and JSON across representative Node.js workloads. -<img src="../benchmarks/javascript/throughput.png" width="90%"/> + Note: Results depend on hardware, dataset, and runtime versions. See the [JavaScript benchmark report](../benchmarks/javascript/README.md) for details. @@ -102,7 +102,7 @@ Note: Results depend on hardware, dataset, and runtime versions. See the Fory Dart demonstrates strong performance compared to Protocol Buffers across representative object and list workloads. -<img src="../benchmarks/dart/throughput.png" width="90%"/> + Note: Results depend on hardware, dataset, and runtime versions. See the [Dart benchmark report](../benchmarks/dart/README.md) for details. diff --git a/versioned_docs/version-0.17/start/install.md b/versioned_docs/version-0.17/start/install.md index 5c3d3e37fe..12249698b3 100644 --- a/versioned_docs/version-0.17/start/install.md +++ b/versioned_docs/version-0.17/start/install.md @@ -134,6 +134,24 @@ cd packages/hps npm run build ``` +## Dart + +Add Apache Fory™ Dart to `pubspec.yaml`: + +```yaml +dependencies: + fory: ^0.17.0 + +dev_dependencies: + build_runner: ^2.4.13 +``` + +Generate serializers after defining annotated types: + +```bash +dart run build_runner build --delete-conflicting-outputs +``` + ## C\# Install the `Apache.Fory` NuGet package. It includes both the runtime and the source generator for `[ForyObject]` types. diff --git a/versioned_docs/version-0.17/start/usage.md b/versioned_docs/version-0.17/start/usage.md index 4fa4a2f0b0..58481ca792 100644 --- a/versioned_docs/version-0.17/start/usage.md +++ b/versioned_docs/version-0.17/start/usage.md @@ -6,55 +6,44 @@ sidebar_position: 1 This section provides quick examples for getting started with Apache Fory™. -## Native Serialization +## Choose A Mode -**Always use native mode when working with a single language.** Native mode delivers optimal performance by avoiding the type metadata overhead required for cross-language compatibility. +Apache Fory™ has two wire modes: -Xlang mode introduces additional metadata encoding costs and restricts serialization to types that are common across all supported languages. Language-specific types will be rejected during serialization in xlang mode. +- **Xlang mode** is the default and the portable format for payloads shared across languages. Use it for cross-language services and for runtimes that expose only xlang mode: Dart, JavaScript/TypeScript, C#, and Swift. +- **Native mode** is selected with `xlang=false` or the equivalent builder option in Java, Scala, Kotlin, Python, C++, Go, and Rust. Use it for same-language traffic because it follows the runtime's native type system, supports a broader language-specific object surface, and is optimized for that runtime. -### Java Serialization +Xlang/default usage uses schema-compatible mode by default. Native mode uses schema-consistent payloads by default unless compatible mode is enabled explicitly. -When you do not need cross-language support, use Java mode for optimal performance. +## Xlang Mode -This example creates a reusable Java-mode runtime, registers a user class, and then performs a basic serialize/deserialize round trip. In production code, keep the `Fory` instance alive and reuse it across requests instead of rebuilding it for every object. +Use xlang mode when bytes need to cross runtime boundaries. Register custom types with the same numeric ID or namespace/type name on every peer. + +Dual-mode runtimes set the xlang option explicitly in the examples below. Dart, JavaScript/TypeScript, C#, and Swift are xlang-only, so their examples do not show an xlang switch. + +### Java ```java -import org.apache.fory.*; -import org.apache.fory.config.*; +import org.apache.fory.Fory; -public class Example { - public static class Person { - String name; - int age; - } +public class XlangExample { + public record Person(String name, int age) {} public static void main(String[] args) { - // Create a Fory instance once and reuse it. - BaseFory fory = Fory.builder() - .withLanguage(Language.JAVA) - .requireClassRegistration(true) - // Replace `build` with `buildThreadSafeFory` for thread-safe usage. - .build(); - fory.register(Person.class); - - Person person = new Person(); - person.name = "chaokunyang"; - person.age = 28; + Fory fory = Fory.builder() + .withXlang(true) + .build(); + fory.register(Person.class, "example", "Person"); + Person person = new Person("chaokunyang", 28); byte[] bytes = fory.serialize(person); Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name + " " + result.age); + System.out.println(result.name() + " " + result.age()); } } ``` -For detailed Java usage including compatibility modes, compression, and advanced features, see [Java Serialization Guide](../guide/java/index.md). - -### Python Serialization - -Python native mode provides a high-performance drop-in replacement for `pickle` and `cloudpickle`. - -The example below uses a dataclass with explicit integer typing so Fory can preserve the intended schema efficiently. As with other runtimes, create the `Fory` instance once, register your types once, and then reuse it for repeated serialization. +### Python ```python from dataclasses import dataclass @@ -63,10 +52,10 @@ import pyfory @dataclass class Person: name: str - age: pyfory.int32 + age: pyfory.Int32 -fory = pyfory.Fory() -fory.register_type(Person) +fory = pyfory.Fory(xlang=True) +fory.register(Person, typename="example.Person") person = Person(name="chaokunyang", age=28) data = fory.serialize(person) @@ -74,13 +63,43 @@ result = fory.deserialize(data) print(result.name, result.age) ``` -For detailed Python usage including type hints, compatibility modes, and advanced features, see [Python Guide](../guide/python/index.md). +### Dart + +```dart +import 'package:fory/fory.dart'; -### Go Serialization +part 'person.fory.dart'; -Go native mode is the default. Register your structs once, then reuse the same `Fory` instance. +@ForyStruct() +class Person { + Person(); -The Go runtime works naturally with exported struct fields and explicit type registration. This snippet shows the standard flow: create `Fory`, register a struct type, serialize a value, and deserialize into a destination struct. + String name = ''; + + @ForyField(type: Int32Type()) + int age = 0; +} + +void main() { + final fory = Fory(); + PersonFory.register( + fory, + Person, + namespace: 'example', + typeName: 'Person', + ); + + final person = Person() + ..name = 'chaokunyang' + ..age = 28; + + final bytes = fory.serialize(person); + final result = fory.deserialize<Person>(bytes); + print('${result.name} ${result.age}'); +} +``` + +### Go ```go package main @@ -97,7 +116,7 @@ type Person struct { } func main() { - f := fory.New() + f := fory.New(fory.WithXlang(true)) if err := f.RegisterStruct(Person{}, 1); err != nil { panic(err) } @@ -117,66 +136,7 @@ func main() { } ``` -For detailed Go usage including configuration, struct tags, and schema evolution, see [Go Guide](../guide/go/index.md). - -### C# Serialization - -C# native serialization uses the `Apache.Fory` runtime together with `[ForyObject]` model types. - -In C#, the usual pattern is to mark your model with `[ForyObject]`, build a runtime once, and register the type before use. The example demonstrates the strongly typed `Serialize` and `Deserialize<T>` APIs that fit normal .NET application code. - -```csharp -using Apache.Fory; - -[ForyObject] -public sealed class Person -{ - public string Name { get; set; } = string.Empty; - public int Age { get; set; } -} - -Fory fory = Fory.Builder().Build(); -fory.Register<Person>(1); - -Person person = new() { Name = "chaokunyang", Age = 28 }; -byte[] data = fory.Serialize(person); -Person result = fory.Deserialize<Person>(data); - -Console.WriteLine($"{result.Name} {result.Age}"); -``` - -For detailed C# usage including source generators, references, and schema evolution, see [C# Guide](../guide/csharp/index.md). - -### Swift Serialization - -Swift native serialization uses `@ForyObject` models and the `Fory` runtime directly. - -Swift uses macro-based model definitions, so the example starts by annotating the type with `@ForyObject`, then registers the type ID and performs a typed round trip. This is the recommended starting point for app-side Swift usage. - -```swift -import Fory - -@ForyObject -struct Person: Equatable { - var name: String = "" - var age: Int32 = 0 -} - -let fory = Fory() -fory.register(Person.self, id: 1) - -let person = Person(name: "chaokunyang", age: 28) -let data = try fory.serialize(person) -let result: Person = try fory.deserialize(data) - -print("\(result.name) \(result.age)") -``` - -For detailed Swift usage including polymorphism, schema evolution, and troubleshooting, see [Swift Guide](../guide/swift/). - -### Rust Serialization - -Rust native mode uses `Fory::default()` and derive macros for compile-time type-safe serialization. The normal pattern is to derive `ForyObject`, register the type once, and then reuse the configured runtime for repeated serialization. +### Rust ```rust use fory::{Error, Fory, ForyObject}; @@ -188,8 +148,8 @@ struct Person { } fn main() -> Result<(), Error> { - let mut fory = Fory::default(); - fory.register::<Person>(1)?; + let mut fory = Fory::builder().xlang(true).build(); + fory.register_by_name::<Person>("example", "Person")?; let person = Person { name: "chaokunyang".to_string(), @@ -203,13 +163,12 @@ fn main() -> Result<(), Error> { } ``` -For detailed Rust usage including references, polymorphism, and row format support, see [Rust Guide](../guide/rust/index.md). - -### C++ Serialization - -C++ native mode uses the `FORY_STRUCT` macro to describe serializable fields and a configured `Fory` runtime to encode and decode values. For single-language C++ usage, set `xlang(false)` explicitly so the runtime stays in native mode. +### C++ ```cpp +#include <cassert> +#include <string> + #include "fory/serialization/fory.h" using namespace fory::serialization; @@ -226,41 +185,30 @@ struct Person { }; int main() { - auto fory = Fory::builder().xlang(false).build(); + auto fory = Fory::builder().xlang(true).build(); fory.register_struct<Person>(1); Person person{"chaokunyang", 28}; - - auto bytes = fory.serialize(person); - auto result = fory.deserialize<Person>(bytes.value()); - assert(result.ok()); - assert(person == result.value()); + auto bytes = fory.serialize(person).value(); + auto result = fory.deserialize<Person>(bytes).value(); + assert(person == result); return 0; } ``` -For detailed C++ usage including `FORY_STRUCT`, thread safety, and schema evolution, see [C++ Guide](../guide/cpp/index.md). - -### Scala Serialization - -Scala native mode provides optimized serialization for Scala-specific types including case classes, collections, and `Option`. - -For Scala projects, register the Scala serializers first so Fory understands Scala-specific data structures correctly. After that, you can register your case classes and use the same core API as the Java runtime. +### Scala ```scala import org.apache.fory.Fory -import org.apache.fory.config.Language -import org.apache.fory.serializer.scala.ScalaSerializers +import org.apache.fory.scala.ForyScala case class Person(name: String, age: Int) object Example { def main(args: Array[String]): Unit = { - val fory = Fory.builder() - .withLanguage(Language.JAVA) - .requireClassRegistration(true) + val fory: Fory = ForyScala.builder() + .withXlang(true) .build() - ScalaSerializers.registerSerializers(fory) fory.register(classOf[Person]) val bytes = fory.serialize(Person("chaokunyang", 28)) @@ -270,27 +218,19 @@ object Example { } ``` -For detailed Scala usage including collection serialization and integration patterns, see [Scala Guide](../guide/scala/index.md). - -### Kotlin Serialization - -Kotlin native mode provides optimized serialization for Kotlin-specific types including data classes, nullable types, and Kotlin collections. - -Kotlin follows the same builder flow as Java, with an extra registration step for Kotlin-specific serializers. The example uses a data class and shows the minimal setup needed for efficient native serialization. +### Kotlin ```kotlin -import org.apache.fory.Fory -import org.apache.fory.config.Language -import org.apache.fory.serializer.kotlin.KotlinSerializers +import org.apache.fory.ThreadSafeFory +import org.apache.fory.kotlin.ForyKotlin data class Person(val name: String, val age: Int) fun main() { - val fory = Fory.builder() - .withLanguage(Language.JAVA) + val fory: ThreadSafeFory = ForyKotlin.builder() + .withXlang(true) .requireClassRegistration(true) - .build() - KotlinSerializers.registerSerializers(fory) + .buildThreadSafeFory() fory.register(Person::class.java) val bytes = fory.serialize(Person("chaokunyang", 28)) @@ -299,133 +239,21 @@ fun main() { } ``` -For detailed Kotlin usage including null safety and default value support, see [kotlin/README.md](https://github.com/apache/fory/blob/main/kotlin/README.md). - -## Cross-Language Serialization +### JavaScript / TypeScript -**Only use xlang mode when you need cross-language data exchange.** Xlang mode adds type metadata overhead for cross-language compatibility and only supports types that can be mapped across all languages. - -The examples below use the same `Person` schema across multiple runtimes. In every language, enable xlang mode and register the type with the same ID or the same fully qualified name. - -### Java - -Java xlang usage is the baseline pattern for JVM services. Enable `Language.XLANG`, register the type with a stable ID or name, and make sure every peer language uses the same mapping. - -```java -import org.apache.fory.*; -import org.apache.fory.config.*; - -public class XlangExample { - public record Person(String name, int age) {} - - public static void main(String[] args) { - Fory fory = Fory.builder() - .withLanguage(Language.XLANG) - .build(); - - fory.register(Person.class, 1); - // fory.register(Person.class, "example.Person"); - - Person person = new Person("chaokunyang", 28); - byte[] bytes = fory.serialize(person); - Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name() + " " + result.age()); - } -} -``` - -### Go - -Go xlang mode is enabled through `WithXlang(true)`. The important part is not the Go syntax itself, but keeping the registered type identity aligned with every other language that reads or writes the payload. - -```go -package main - -import ( - "fmt" - - "github.com/apache/fory/go/fory" -) - -type Person struct { - Name string - Age int32 -} - -func main() { - f := fory.New(fory.WithXlang(true)) - if err := f.RegisterStruct(Person{}, 1); err != nil { - panic(err) - } - - person := &Person{Name: "chaokunyang", Age: 28} - data, err := f.Serialize(person) - if err != nil { - panic(err) - } - - var result Person - if err := f.Deserialize(data, &result); err != nil { - panic(err) - } - - fmt.Printf("%s %d\n", result.Name, result.Age) -} -``` - -### Rust - -Rust follows the same cross-language contract, but expresses it through derived traits and explicit registration on the `Fory` instance. Once the type ID matches the other runtimes, the payload can move across language boundaries safely. - -```rust -use fory::{Fory, ForyObject}; -use std::error::Error; - -#[derive(ForyObject, Debug)] -struct Person { - name: String, - age: i32, -} - -fn main() -> Result<(), Box<dyn Error>> { - let mut fory = Fory::default().xlang(true); - fory.register::<Person>(1)?; - // fory.register_by_name::<Person>("example.Person")?; - - let person = Person { - name: "chaokunyang".to_string(), - age: 28, - }; - let bytes = fory.serialize(&person); - let result: Person = fory.deserialize(&bytes)?; - println!("{} {}", result.name, result.age); - Ok(()) -} -``` - -### JavaScript - -JavaScript cross-language support is schema-driven. Instead of registering a class, you describe the payload shape with `Type.object(...)`, then use the returned serializer pair to encode and decode values. - -These packages are not published to npm yet. Build them from the Apache Fory repository first, then use the following API shape. - -```javascript +```typescript import Fory, { Type } from "@apache-fory/core"; -/** - * `@apache-fory/hps` uses V8 fast calls directly from JIT. - * Use Node.js 20+ when enabling it. - * If installation fails, replace it with `const hps = null;`. - */ -import hps from "@apache-fory/hps"; - -const description = Type.object("example.Person", { - name: Type.string(), - age: Type.int32(), -}); +const personType = Type.struct( + { typeName: "example.Person" }, + { + name: Type.string(), + age: Type.int32(), + }, +); -const fory = new Fory({ hps }); -const { serialize, deserialize } = fory.registerSerializer(description); +const fory = new Fory(); +const { serialize, deserialize } = fory.register(personType); const payload = serialize({ name: "chaokunyang", age: 28 }); const result = deserialize(payload); @@ -434,8 +262,6 @@ console.log(result); ### C\# -C# cross-language code looks similar to native usage, but the runtime is explicitly configured for xlang and compatible mode. Use the same type ID or namespace/name mapping as your Java, Go, Swift, or Rust peers. - ```csharp using Apache.Fory; @@ -446,34 +272,28 @@ public sealed class Person public int Age { get; set; } } -Fory fory = Fory.Builder() - .Xlang(true) - .Compatible(true) - .Build(); - +Fory fory = Fory.Builder().Build(); fory.Register<Person>(1); Person person = new() { Name = "chaokunyang", Age = 28 }; -byte[] payload = fory.Serialize(person); -Person result = fory.Deserialize<Person>(payload); +byte[] data = fory.Serialize(person); +Person result = fory.Deserialize<Person>(data); Console.WriteLine($"{result.Name} {result.Age}"); ``` ### Swift -Swift cross-language serialization uses the same `@ForyObject` model style as native mode, but you create the runtime with `xlang: true`. Stable registration IDs are still the key requirement for interoperability. - ```swift import Fory -@ForyObject +@ForyStruct struct Person: Equatable { var name: String = "" var age: Int32 = 0 } -let fory = Fory(xlang: true, trackRef: false, compatible: true) +let fory = Fory() fory.register(Person.self, id: 1) let person = Person(name: "chaokunyang", age: 28) @@ -483,20 +303,91 @@ let result: Person = try fory.deserialize(data) print("\(result.name) \(result.age)") ``` -### Key Points +For more cross-language rules and examples, see: -- Enable xlang mode in every runtime (`Language.XLANG`, `WithXlang(true)`, `Xlang(true)`, `Fory(xlang: true, ...)`, and so on). -- Register types with **consistent IDs or names** across all languages. -- ID-based registration is more compact and faster, but it requires coordination to avoid conflicts. -- Name-based registration is easier to manage across teams, but it produces slightly larger payloads. -- Only use types that have cross-language mappings; see [Type Mapping](../specification/xlang_type_mapping.md). +- [Cross-Language Serialization Guide](../guide/xlang/index.md) +- [Java Guide](../guide/java/index.md) +- [Python Guide](../guide/python/index.md) +- [Dart Guide](../guide/dart/index.md) +- [Go Guide](../guide/go/index.md) +- [Rust Guide](../guide/rust/index.md) +- [C++ Guide](../guide/cpp/index.md) +- [C# Guide](../guide/csharp/index.md) +- [Swift Guide](../guide/swift/index.md) -For examples with circular references, shared references, and polymorphism across languages, see: +## Native Mode -- [Cross-Language Serialization Guide](../guide/xlang/index.md) -- [Go Guide - Cross Language](../guide/go/cross-language.md) -- [C# Guide - Cross Language](../guide/csharp/cross-language.md) -- [Swift Guide - Cross Language](../guide/swift/cross_language) +Use native mode only when every reader and writer is the same runtime family. Native mode supports broader language-specific object models than portable xlang mappings and is optimized for the owning runtime. + +Java and Python native modes are first-class same-language entry points. Use Java native mode when replacing JDK serialization, Kryo, FST, Hessian, or Java-only Protocol Buffers payloads. Use Python native mode when replacing `pickle` or `cloudpickle` for Python-only payloads. + +Dart, JavaScript/TypeScript, C#, and Swift do not expose native mode. + +### Java + +```java +Fory fory = Fory.builder() + .withXlang(false) + .requireClassRegistration(true) + .build(); +``` + +Register Java classes and use `serialize` / `deserialize` as usual. See the [Java Guide](../guide/java/index.md) for Java object hooks, `Externalizable`, dynamic object graphs, object copy, and Java native-mode zero-copy buffers. + +### Python + +```python +import pyfory + +fory = pyfory.Fory(xlang=False, ref=False, strict=True) +``` + +Register Python classes and use `serialize` / `deserialize` as usual. See the [Python Guide](../guide/python/index.md) for native-mode pickle replacement behavior and security settings. + +### Go + +```go +f := fory.New(fory.WithXlang(false)) +``` + +Use native mode for Go-only structs, pointers, interfaces, and Go-specific type behavior. See the [Go Guide](../guide/go/index.md) for struct tags and native-mode configuration. + +### Rust + +```rust +let mut fory = Fory::builder().xlang(false).build(); +``` + +Use native mode for Rust-only payloads that rely on Rust-specific object behavior. See the [Rust Guide](../guide/rust/index.md) for derive, references, and supported types. + +### C++ + +```cpp +auto fory = Fory::builder().xlang(false).build(); +``` + +Use native mode for C++-only traffic that does not need portable xlang type mappings. See the [C++ Guide](../guide/cpp/index.md) for `FORY_STRUCT`, configuration, and schema metadata. + +### Scala + +```scala +val fory = ForyScala.builder() + .withXlang(false) + .build() +``` + +Use native mode for Scala/JVM-only traffic that needs Scala case classes, collections, tuples, options, or enums on the JVM runtime path. See the [Scala Guide](../guide/scala/index.md). + +### Kotlin + +```kotlin +val fory = ForyKotlin.builder() + .withXlang(false) + .requireClassRegistration(true) + .buildThreadSafeFory() +``` + +Use native mode for Kotlin/JVM-only traffic that needs Kotlin data classes, nullable types, ranges, unsigned values, or Kotlin collections on the JVM runtime path. See the [Kotlin Guide](../guide/kotlin/index.md). ## Row Format Encoding --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
