Rust宏代码生成库o2o-macros的使用,实现自动化对象转换与序列化功能
Rust宏代码生成库o2o-macros的使用,实现自动化对象转换与序列化功能
安装
在项目目录中运行以下Cargo命令:
cargo add o2o-macros
或者在Cargo.toml中添加以下行:
o2o-macros = "0.5.4"
使用示例
o2o-macros是一个Rust宏库,用于自动化对象转换和序列化功能。下面是一个完整的使用示例:
use o2o_macros::o2o;
use serde::{Serialize, Deserialize};
// 定义源结构体
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
}
// 定义目标结构体并使用o2o宏自动生成转换代码
#[derive(Serialize, Deserialize)]
#[o2o(from(Person))]
struct PersonDto {
name: String,
#[map(age)]
years_old: u8,
}
fn main() {
// 创建一个Person实例
let person = Person {
name: "Alice".to_string(),
age: 30,
};
// 自动转换为PersonDto
let dto: PersonDto = person.into();
println!("Name: {}, Age: {}", dto.name, dto.years_old);
// 序列化为JSON
let json = serde_json::to_string(&dto).unwrap();
println!("JSON: {}", json);
// 反序列化
let deserialized: PersonDto = serde_json::from_str(&json).unwrap();
println!("Deserialized: {} {}", deserialized.name, deserialized.years_old);
}
主要特性
- 自动对象转换:使用
#[o2o]
宏自动生成结构体之间的转换代码 - 字段映射:使用
#[map]
属性重命名字段 - 序列化支持:与serde无缝集成
- 类型转换:支持基本类型的自动转换
更复杂的示例
use o2o_macros::o2o;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Address {
street: String,
city: String,
zip_code: String,
}
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
address: Address,
}
#[derive(Serialize, Deserialize)]
#[o2o(from(Person))]
struct PersonDto {
full_name: String, // 重命名字段
#[map(age)]
years_old: u8, // 重命名字段
#[map(address.street)] // 嵌套字段映射
street: String,
#[map(address.city)]
city: String,
#[map(address.zip_code)]
postal_code: String, // 重命名嵌套字段
}
fn main() {
let person = Person {
name: "Bob".to_string(),
age: 25,
address: Address {
street: "123 Main St".to_string(),
city: "Anytown".to_string(),
zip_code: "12345".to_string(),
},
};
let dto: PersonDto = person.into();
println!("Person: {} ({}), {} {}, {}",
dto.full_name,
dto.years_old,
dto.street,
dto.city,
dto.postal_code
);
}
1 回复
Rust宏代码生成库o2o-macros的使用指南
概述
o2o-macros是一个Rust宏代码生成库,主要用于简化对象之间的转换和序列化操作。它通过过程宏自动生成转换代码,减少手动编写样板代码的工作量。
主要功能
- 自动生成对象之间的转换代码
- 简化序列化和反序列化过程
- 支持字段映射和自定义转换逻辑
安装
在Cargo.toml中添加依赖:
[dependencies]
o2o-macros = "0.1" # 请使用最新版本
serde = { version = "1.0", features = ["derive"] } # 如果需要序列化功能
基本用法
1. 简单对象转换
use o2o_macros::o2o;
#[derive(o2o)]
#[from(Source)]
struct Target {
#[map(name)]
full_name: String,
age: i32,
#[map(address.street)]
street: String,
}
struct Source {
name: String,
age: i32,
address: Address,
}
struct Address {
street: String,
}
// 自动生成的转换代码允许你这样使用:
let source = Source {
name: "Alice".to_string(),
age: 30,
address: Address {
street: "123 Main St".to_string(),
},
};
let target: Target = source.into();
2. 双向转换
use o2o_macros::o2o;
#[derive(o2o)]
#[o2o(
from(Source),
into(Source)
)]
struct Target {
#[map(name)]
full_name: String,
age: i32,
}
struct Source {
name: String,
age: i32,
}
// 现在可以在两个方向转换:
let source = Source {
name: "Bob".to_string(),
age: 25,
};
let target: Target = source.into();
let original_source: Source = target.into();
3. 自定义转换逻辑
use o2o_macros::o2o;
#[derive(o2o)]
#[from(Source)]
struct Target {
#[map(name)]
full_name: String,
age: i32,
#[map(|s: &Source| s.scores.iter().sum())]
total_score: i32,
}
struct Source {
name: String,
age: i32,
scores: Vec<i32>,
}
let source = Source {
name: "Charlie".to_string(),
age: 28,
scores: vec![85, 90, 78],
};
let target: Target = source.into();
println!("Total score: {}", target.total_score); // 输出: Total score: 253
4. 与serde集成
use o2o_macros::o2o;
use serde::{Serialize, Deserialize};
#[derive(o2o, Serialize, Deserialize)]
#[from(SourceDto)]
struct DomainModel {
#[map(user_id)]
id: u64,
#[map(user_name)]
name: String,
#[map(|dto: &SourceDto| dto.age.unwrap_or(0))]
age: i32,
}
#[derive(Serialize, Deserialize)]
struct SourceDto {
user_id: u64,
user_name: String,
age: Option<i32>,
}
// 可以从JSON反序列化并转换为领域模型
let json = r#"{"user_id": 123, "user_name": "Dave", "age": 35}"#;
let dto: SourceDto = serde_json::from_str(json).unwrap();
let model: DomainModel = dto.into();
// 也可以反向转换并序列化
let new_dto: SourceDto = model.into();
let json_output = serde_json::to_string(&new_dto).unwrap();
高级特性
1. 忽略字段
#[derive(o2o)]
#[from(Source)]
struct Target {
name: String,
#[o2o(ignore)]
ignored_field: String, // 不会从Source映射
}
2. 类型转换
#[derive(o2o)]
#[from(Source)]
struct Target {
#[map(as String)]
name: String, // 自动调用to_string()
#[map(age as f32)]
age_float: f32,
}
3. 嵌套结构转换
#[derive(o2o)]
#[from(Source)]
struct Target {
name: String,
#[map(address)]
home_address: Address,
}
#[derive(o2o)]
#[from(SourceAddress)]
struct Address {
street: String,
city: String,
}
struct Source {
name: String,
address: SourceAddress,
}
struct SourceAddress {
street: String,
city: String,
}
完整示例
下面是一个结合多个特性的完整示例:
use o2o_macros::o2o;
use serde::{Serialize, Deserialize};
// 源数据结构
#[derive(Debug, Serialize, Deserialize)]
struct UserDto {
user_id: u32,
username: String,
birth_year: u16,
is_active: bool,
#[serde(skip_serializing_if = "Option::is_none")]
last_login: Option<String>,
preferences: PreferencesDto,
}
#[derive(Debug, Serialize, Deserialize)]
struct PreferencesDto {
theme: String,
notifications_enabled: bool,
}
// 目标数据结构
#[derive(Debug, o2o, Serialize, Deserialize)]
#[o2o(
from(UserDto),
into(UserDto)
)]
struct User {
#[map(user_id)]
id: u32,
#[map(username)]
name: String,
#[map(|dto: &UserDto| 2023 - dto.birth_year as i32)] // 计算年龄
age: i32,
#[map(is_active)]
active: bool,
#[o2o(ignore)] // 忽略字段
metadata: String,
#[map(preferences)]
settings: Preferences,
}
#[derive(Debug, o2o, Serialize, Deserialize)]
#[from(PreferencesDto)]
struct Preferences {
#[map(theme)]
color_scheme: String,
#[map(notifications_enabled)]
notify: bool,
}
fn main() {
// JSON数据
let json_data = r#"
{
"user_id": 12345,
"username": "john_doe",
"birth_year": 1990,
"is_active": true,
"preferences": {
"theme": "dark",
"notifications_enabled": true
}
}
"#;
// 从JSON反序列化为DTO
let dto: UserDto = serde_json::from_str(json_data).unwrap();
println!("Source DTO: {:?}", dto);
// 转换为领域模型
let user: User = dto.into();
println!("Domain model: {:?}", user);
// 添加一些元数据
let mut user_with_meta = user;
user_with_meta.metadata = "extra info".to_string();
// 转换回DTO
let new_dto: UserDto = user_with_meta.into();
println!("New DTO: {:?}", new_dto);
// 序列化为JSON
let json_output = serde_json::to_string_pretty(&new_dto).unwrap();
println!("JSON output:\n{}", json_output);
}
最佳实践
- 对于大型项目,考虑将DTO(数据传输对象)和领域模型分开
- 使用
#[o2o(ignore)]
显式忽略不需要映射的字段 - 对于复杂转换逻辑,使用闭包表达式提供自定义映射
- 结合serde实现完整的序列化/反序列化管道
o2o-macros通过减少样板代码显著提高了开发效率,特别是在需要频繁在不同对象表示之间转换的场景中。