Apache Fory 团队很高兴地宣布 0.13.1 版本发布。此版本包含来自 11 位不同贡献者的 28 个 PR。请参阅安装页面了解如何获取适用于您平台的库。
✨ 亮点
- 🔧 支持 Rust 枚举变体以及元组/结构体风格枚举的 schema 演进
- 🎯 支持 Rust 元组序列化和 schema 演进
- ⏭️ 支持 Rust skip 宏属性
🎨 枚举 Schema 演进
Fory v0.13.1 在兼容模式下增加了全面的枚举 schema 演进支持,支持所有三种变体类型(Unit、Unnamed、Named):
- ➕ 添加/删除变体:未知变体回退到
#[fory(default)] - 📝 添加/删除字段:命名变体支持字段演进,自动使用默认值
- 🔄 修改元素:未命名变体处理元素数量变化(多余的被跳过,缺失的使用默认值)
- 🔀 变体类型更改:在 Unit/Unnamed/Named 之间转换,自动使用默认值
// 版本 1
#[derive(ForyObject)]
enum Command {
#[fory(default)]
Noop,
Execute { name: String, args: i32 },
}
// 版本 2 - 添加了字段和新变体
// 同时支持 `fory(default)` 和标准 `default`
#[derive(Default, ForyObject)]
enum Command {
#[default]
Noop,
Execute { name: String, args: i32, env: String }, // 添加了 'env'
Cancel { reason: String }, // 新变体
}
// V1→V2: 缺失的 'env' 获得默认值 ""; Cancel→Noop 回退到 V1
// V2→V1: 额外的 'env' 被跳过; Cancel→Noop 回退
📦 元组 Schema 演进
元组(1-22 个元素)现在在兼容模式下支持长度演进:
- 📏 长度变化:增大或缩小元组大小(缺失的元素获得默认值,多余的被丢弃)
- 📚 集合:完全支持
Vec、HashMap、HashSet元素 - 🪆 嵌套元组:多级嵌套,每层独立演进
- 🔗 智能指针:
Option、Arc、Rc包装的元素正确处理演进 - 🏗️ 结构体字段:结构体中的元组字段独立演进
let fory = Fory::default().compatible(true);
// 序列化 2 元素元组
let short = (42i32, "hello".to_string());
let bin = fory.serialize(&short).unwrap();
// 反序列化为 4 元素元组 - 额外的元素获得默认值
let long: (i32, String, f64, bool) = fory.deserialize(&bin).unwrap();
assert_eq!(long, (42, "hello".to_string(), 0.0, false));
// 反向:4→2 元素,多余的被丢弃
let long = (100i32, "world".to_string(), 3.14, true);
let bin = fory.serialize(&long).unwrap();
let short: (i32, String) = fory.deserialize(&bin).unwrap();
assert_eq!(short, (100, "world".to_string()));
📦 快速开始
默认情况下会使用各语言的原生模式以获得最佳性能;只有在需要跨语言互通时,才将序列化模式切换为 xlang=true(或等效选项)。
Rust
[dependencies]
fory = "0.13"
use fory::{Error, Fory};
use fory::ForyObject;
#[derive(ForyObject, Debug, PartialEq)]
struct Person {
name: String,
age: i32,
}
fn main() -> Result<(), Error> {
let mut fory = Fory::default();
fory.register::<Person>(1)?;
let bytes = fory.serialize(&Person { name: "Alice".into(), age: 30 })?;
let decoded: Person = fory.deserialize(&bytes)?;
assert_eq!(decoded.age, 30);
Ok(())
}
Python
python -m pip install --upgrade pip
pip install pyfory==0.13.1
from dataclasses import dataclass
import pyfory
@dataclass
class Person:
name: str
age: pyfory.int32
fory = pyfory.Fory()
fory.register_type(Person)
payload = fory.serialize(Person(name="Alice", age=30))
decoded = fory.deserialize(payload)
print(decoded.name, decoded.age)
Java
<dependency>
<groupId>org.apache.fory</groupId>
<artifactId>fory-core</artifactId>
<version>0.13.1</version>
</dependency>
import org.apache.fory.*;
import org.apache.fory.config.*;
public class QuickStart {
public static class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
Fory fory = Fory.builder()
.withLanguage(Language.JAVA)
.requireClassRegistration(true)
.build();
fory.register(Person.class);
byte[] bytes = fory.serialize(new Person("Alice", 30));
Person result = (Person) fory.deserialize(bytes);
System.out.println(result.name + " " + result.age);
}
}
Scala
// SBT Scala 2.13
libraryDependencies += "org.apache.fory" % "fory-scala_2.13" % "0.13.1"
// SBT Scala 3
libraryDependencies += "org.apache.fory" % "fory-scala_3" % "0.13.1"
import org.apache.fory.Fory
import org.apache.fory.config.Language
import org.apache.fory.serializer.scala.ScalaSerializers
case class Person(name: String, age: Int)
object Example {
def main(args: Array[String]): Unit = {
val fory = Fory.builder()
.withLanguage(Language.JAVA)
.requireClassRegistration(true)
.build()
ScalaSerializers.registerSerializers(fory)
fory.register(classOf[Person])
val bytes = fory.serialize(Person("Alice", 30))
val result = fory.deserialize(bytes).asInstanceOf[Person]
println(s"${result.name} ${result.age}")
}
}
Kotlin
<dependency>
<groupId>org.apache.fory</groupId>
<artifactId>fory-kotlin</artifactId>
<version>0.13.1</version>
</dependency>
import org.apache.fory.Fory
import org.apache.fory.config.Language
import org.apache.fory.serializer.kotlin.KotlinSerializers
data class Person(val name: String, val age: Int)
fun main() {
val fory = Fory.builder()
.withLanguage(Language.JAVA)
.requireClassRegistration(true)
.build()
KotlinSerializers.registerSerializers(fory)
fory.register(Person::class.java)
val bytes = fory.serialize(Person("Alice", 30))
val result = fory.deserialize(bytes) as Person
println("${result.name} ${result.age}")
}
Golang
package main
import (
"fmt"
forygo "github.com/apache/fory/go/fory"
)
type Person struct {
Name string
Age int32
}
func main() {
fory := forygo.NewFory(true)
fory.Register(Person{}, 1)
payload, _ := fory.Marshal(Person{Name: "Alice", Age: 30})
var decoded Person
fory.Unmarshal(payload, &decoded)
fmt.Println(decoded.Name, decoded.Age)
}
JavaScript
import Fory, { Type } from "@apache-fory/fory";
import hps from "@apache-fory/hps";
const description = Type.object("example.Person", {
name: Type.string(),
age: Type.int32(),
});
const fory = new Fory({ hps });
const { serialize, deserialize } = fory.registerSerializer(description);
const payload = serialize({ name: "Alice", age: 30 });
const decoded = deserialize(payload);
console.log(decoded.name, decoded.age);
🎁 新特性
- 📊 feat(rust): 添加 rust benchmark 报告脚本和结果 by @chaokunyang in https://github.com/apache/fory/pull/2835
- 📏 feat(rust): rust benchmark 打印序列化数据大小 by @chaokunyang in https://github.com/apache/fory/pull/2845
- 🏷️ feat(rust): 支持 rust tagged union 枚举 by @urlyy in https://github.com/apache/fory/pull/2855
- 🔢 feat(rust): 支持 rust 无符号数字 by @chaokunyang in https://github.com/apache/fory/pull/2857
- 📦 feat(rust): 支持 rust 元组序列化和 schema 演进 by @chaokunyang in https://github.com/apache/fory/pull/2858
- 🔄 feat(go): 实现新的字段排序和类型哈希算法 by @ThisingL in https://github.com/apache/fory/pull/2868
- ⏭️ feat: 支持 fory skip 宏属性(#2864) by @kitty-eu-org in https://github.com/apache/fory/pull/2865
- 📐 feat(Rust): 支持 usize by @urlyy in https://github.com/apache/fory/pull/2870
- ⚡ feat(rust): 使是否写入 type/ref 为编译时求值 by @chaokunyang in https://github.com/apache/fory/pull/2871
- 📋 feat(rust): 为 rust 添加数组支持 by @chaokunyang in https://github.com/apache/fory/pull/2874
- 🎨 feat(rust): 支持 schema 演进模式的枚举变体 by @chaokunyang in https://github.com/apache/fory/pull/2873
🐛 Bug 修复
- 🔧 fix: 修复 0.14.0 快照版本 by @chaokunyang in https://github.com/apache/fory/pull/2842
- 🧵 fix(java): 设置 ForyJitCompilerThreadFactory 生成守护线程 by @coderunner234 in https://github.com/apache/fory/pull/2869
- 🔍 fix: 修改 Fory 中的深度设置以防止重复注册 by @mengnankkkk in https://github.com/apache/fory/pull/2852
🔨 其他改进
- 📝 docs(rust): 更新 rust benchmark 报告表 by @chaokunyang in https://github.com/apache/fory/pull/2836
- 📦 chore(rust): 更新 cargo toml 用于发布 by @chaokunyang in https://github.com/apache/fory/pull/2838
- 🔗 docs: 修复文档同步目标 by @chaokunyang in https://github.com/apache/fory/pull/2839
- ♻️ docs: 重构 readme by @chaokunyang in https://github.com/apache/fory/pull/2843
- ➕ docs: 在 readme 中添加 AGENTS by @chaokunyang in https://github.com/apache/fory/pull/2844
- ➖ chore: 从主 readme 中删除 agents by @chaokunyang in https://github.com/apache/fory/pull/2846
- 📝 docs: 更新 readme by @chaokunyang in https://github.com/apache/fory/pull/2847
- 🔗 docs: 修复 xlang 类型映射链接 by @chaokunyang in https://github.com/apache/fory/pull/2848
- 🧪 chore(Java): 使 RustXlangTest 测试用例相互独立 by @urlyy in https://github.com/apache/fory/pull/2834
- 🗑️ chore(java): 删除打印属性名称 by @Danden1 in https://github.com/apache/fory/pull/2860
- 🐍 chore(python): 添加 py3.13 发布标志 by @chaokunyang in https://github.com/apache/fory/pull/2872
- 🧪 chore(rust): 添加在 macro_rules! 中使用 #[derive(ForyObject)] 的测试 by @REASY in https://github.com/apache/fory/pull/2867
👥 新贡献者
- 🎉 @Danden1 在 https://github.com/apache/fory/pull/2860 中首次贡献
- 🎉 @coderunner234 在 https://github.com/apache/fory/pull/2869 中首次贡献
- 🎉 @REASY 在 https://github.com/apache/fory/pull/2867 中首次贡献
完整变更日志: https://github.com/apache/fory/compare/v0.13.0...v0.13.1
