跳到主要内容

Apache Fory 1.5.0 正式发布

· 阅读需 7 分钟
杨朝坤
Apache Fory PMC Member

Apache Fory 团队很高兴地宣布 1.5.0 版本正式发布。本次发布包含 25 个 PR。请访问安装页面,获取适用于您所用平台的库。

亮点

  • Fory JSON 的性能最高比 Jackson 快 5 倍、比 Gson 快 10 倍。
  • Rust、Dart、Swift 和 C# 现已支持外部类型序列化,可为第三方结构化类型生成序列化器,同时保留常规 xlang 类型标识和编码方式。
  • C# 和 Dart 现已支持类继承;Dart 还支持按需忽略继承的私有字段。

更快的 Fory JSON

Fory 1.5.0 为 Fory JSON 带来显著的性能跃升,对序列化和反序列化路径均进行了深度优化,性能最高比 Jackson 快 5 倍、比 Gson 快 10 倍。

表示形式操作fory-json ops/secJackson ops/secGson ops/sec相比 Jackson相比 Gson
String序列化7,387,4652,049,3681,084,0423.60×6.81×
String反序列化2,897,9551,074,885902,7722.70×3.21×
UTF-8 字节序列化10,375,4981,868,6141,037,2115.55×10.00×
UTF-8 字节反序列化3,077,1581,268,397933,0792.43×3.30×

Rust/Swift/CSharp/Dart 的外部类型序列化

Fory 1.5.0 为 Rust、Dart、Swift 和 C# 新增外部类型序列化。应用可以为无法修改、因而不能添加 Fory 注解的第三方结构化类型定义本地序列化器或 Schema 声明。随后,Fory 可以直接读写目标值,无需包装对象或中间镜像对象。

生成的声明沿用相应运行时常规的注册方式与编码模型。在 xlang 模式下,外部 struct、enum 和 union 使用与直接支持的类型相同的对应类型标识和编码方式。对于私有、不透明或需要维持内部不变量的目标类型,如果结构化代码生成并不合适,仍可使用自定义序列化器。

在 Rust 中,本地 derive 指定目标类型,并在处理根值时显式选择:

use fory::{Fory, ForyStruct};

#[derive(ForyStruct)]
#[fory(target = third_party::User)]
struct UserSerializer {
name: String,
age: u32,
}

let mut fory = Fory::builder().xlang(true).build();
fory.register::<UserSerializer>(100)?;
let bytes = fory.serialize_with::<UserSerializer>(&user)?;
let decoded =
fory.deserialize_with::<UserSerializer>(&bytes)?;

Dart 根据本地 target 声明生成序列化器,再通过生成的模块注册第三方目标类型:

(target: third_party.User)
abstract final class UserSerializer {
(id: 1)
late final String name;

(id: 2, type: Int32Type())
late final int age;
}

ExternalSerializersForyModule.register(
fory,
third_party.User,
id: 100,
);

Swift 在应用继续传入 ThirdParty.User 值的同时,注册并选择外部序列化器:

@ForyStruct(target: ThirdParty.User.self)
struct UserSerializer {
var name: String
var age: UInt32
}

try fory.register(UserSerializer.self, id: 100)
let bytes = try fory.serialize(user, with: UserSerializer.self)
let decoded = try fory.deserialize(bytes, with: UserSerializer.self)

C# 源代码生成使用本地抽象声明,并通过常规 API 注册目标类型:

using Apache.Fory;
using S = Apache.Fory.Schema.Types;

[ForyStruct(Target = typeof(ThirdParty.User))]
internal abstract class UserSerializer
{
[ForyField(1)]
public abstract string Name { get; }

[ForyField(2, Type = typeof(S.Int32))]
public abstract int Age { get; }
}

fory.Register<ThirdParty.User>(100);
byte[] bytes = fory.Serialize(user);

有关构造要求、嵌套容器、动态值和高级映射,请参阅 RustDartSwiftC# 的专门指南。

C# 和 Dart 的类继承支持

Fory 1.5.0 为 C# 和 Dart 新增类继承支持。在这两种运行时中,继承的存储字段和子类的存储字段会表示为一个扁平化 Schema,而不是嵌套的基类对象。因此,常规的字段排序、Schema 演进、引用跟踪和对象图内存检查仍可直接应用于具体类型。

在 C# 中,由于 [ForyStruct] 不会被继承,因此需要直接为每个参与继承的类添加注解。带注解的抽象基类为具体子类提供 Schema 字段,而只需注册具体派生类型:

[ForyStruct]
public abstract class Entity
{
[ForyField(1)]
private long _id;

public long Id => _id;
}

[ForyStruct]
public sealed class User : Entity
{
[ForyField(2)]
public string Name { get; set; } = string.Empty;
}

fory.Register<User>(102);

Dart 代码生成会发现超类和已应用 mix-in 中的存储字段。父类继承而来的公共字段无需注解,具体子类还可以按需忽略继承的私有字段:

class MessageBase {
int sequence = 0;
String _cache = '';
}

(ignoreInheritedPrivateFields: true)
class TextMessage extends MessageBase {
TextMessage();

String text = '';
}

生成的 TextMessage Schema 包含 sequencetext,但不包含继承的私有字段 _cache。该选项不会忽略继承的公共字段,也不会忽略由子类自身声明的私有字段。

有关构造函数规则、跨 package 的私有字段访问、mix-in、泛型、引用和 Schema 兼容性,请参阅 C# 类继承Dart 继承

新功能

问题修复

其他改进

完整变更日志https://github.com/apache/fory/compare/v1.4.0...v1.5.0