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);
}

主要特性

  1. 自动对象转换:使用#[o2o]宏自动生成结构体之间的转换代码
  2. 字段映射:使用#[map]属性重命名字段
  3. 序列化支持:与serde无缝集成
  4. 类型转换:支持基本类型的自动转换

更复杂的示例

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宏代码生成库,主要用于简化对象之间的转换和序列化操作。它通过过程宏自动生成转换代码,减少手动编写样板代码的工作量。

主要功能

  1. 自动生成对象之间的转换代码
  2. 简化序列化和反序列化过程
  3. 支持字段映射和自定义转换逻辑

安装

在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);
}

最佳实践

  1. 对于大型项目,考虑将DTO(数据传输对象)和领域模型分开
  2. 使用#[o2o(ignore)]显式忽略不需要映射的字段
  3. 对于复杂转换逻辑,使用闭包表达式提供自定义映射
  4. 结合serde实现完整的序列化/反序列化管道

o2o-macros通过减少样板代码显著提高了开发效率,特别是在需要频繁在不同对象表示之间转换的场景中。

回到顶部