Rust序列化与反序列化库serde_syn的使用,serde_syn提供语法树解析与代码生成的强大工具

Rust序列化与反序列化库serde_syn的使用

serde_syn是一个用于在过程宏中解析Rust语法的serde后端。它允许您从属性中反序列化自定义derive的参数并直接转换为结构体,目标是消除编写过程宏时的解析样板代码。

使用方式

serde_syn的接口相当简洁:

  • 对于已经解析/访问的属性,可以使用parser函数来处理其中的语法
  • 如果直接处理proc_macro/proc_macro2令牌流或字符串,也应该使用parser函数
  • 如果自己实现syn的Parse trait,应该使用from_stream函数,它接收一个ParseStream

config模块中有许多预制的配置用于常见语法(类似JSON、类似属性、类似表达式等),也可以组合标志来构建自己的配置。

示例代码

以下是内容中提供的derive宏实现示例:

/// The format of `named` attributes.
#[derive(Deserialize)]
struct Props {
    rename: Option<String>, // #[named(rename="hello")]
    lowercase: Option<()>,  // #[named(lowercase)]
}

#[proc_macro_derive(NamedType, attributes(named))]
pub fn my_macro(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let mut name = input.ident.to_string();

    for attr in input.attrs.iter().filter(|a| a.path.is_ident("named")) {
        let parser = parser::<Props>(config::RUSTY_META);
        let props = match attr.parse_args_with(parser) {
            Ok(props) => props,
            Err(err) => return err.to_compile_error().into(),
        };

        if let Some(rename) = props.rename { name = rename; }
        if props.lowercase.is_some() { name = name.to_lowercase(); }
    }

    let ident = &input.ident;
    (quote! {
        impl NamedType for #ident {
            fn name() -> &'static str { #name }
        }
    }).into()
}

完整示例

以下是一个更完整的示例,展示如何使用serde_syn来处理自定义derive属性:

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
use quote::quote;
use serde::Deserialize;

// 定义属性参数的结构体
#[derive(Deserialize)]
struct FieldAttributes {
    skip: Option<()>,      // #[my_attr(skip)]
    rename: Option<String>, // #[my_attr(rename="new_name")]
    default: Option<String>, // #[my_attr(default="value")]
}

#[proc_macro_derive(MyDerive, attributes(my_attr))]
pub fn my_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    
    // 处理结构体/枚举名称
    let name = &input.ident;
    
    // 处理字段
    let fields = if let syn::Data::Struct(s) &input.data {
        &s.fields
    } else {
        unimplemented!("Only structs are supported");
    };
    
    // 为每个字段生成代码
    let field_impls = fields.iter().map(|f| {
        let field_name = &f.ident;
        
        // 检查字段是否有my_attr属性
        let attrs = f.attrs.iter().filter(|a| a.path.is_ident("my_attr"));
        
        let mut skip = false;
        let mut rename = None;
        let mut default = None;
        
        for attr in attrs {
            let parser = serde_syn::parser::<FieldAttributes>(serde_syn::config::RUSTY_META);
            let props = match attr.parse_args_with(parser) {
                Ok(props) => props,
                Err(err) => return err.to_compile_error(),
            };
            
            if props.skip.is_some() {
                skip = true;
            }
            if let Some(r) = props.rename {
                rename = Some(r);
            }
            if let Some(d) = props.default {
                default = Some(d);
            }
        }
        
        if skip {
            quote! {}
        } else {
            let field_ident = rename.unwrap_or_else(|| field_name.as_ref().unwrap().to_string());
            if let Some(def) = default {
                quote! {
                    #field_ident: self.#field_name.unwrap_or(#def.to_string()),
                }
            } else {
                quote! {
                    #field_ident: self.#field_name.to_string(),
                }
            }
        }
    });
    
    // 生成最终实现
    let output = quote! {
        impl MyDerive for #name {
            fn serialize(&self) -> String {
                let mut result = String::new();
                #(#field_impls)*
                result
            }
        }
    };
    
    output.into()
}

错误处理

反序列化错误会自动分配一个"span"(无法解析的源代码区域)作为普通的syn::Error。当错误报告给Rust编译器时,将突出显示正确的代码区域:

error: unknown field `lowrcase`, expected `rename` or `lowercase`
  --> named_type.rs:4:13
   |
4  |     #[named(lowrcase)]
   |             ^^^^^^^^

如果直接使用Deserializer,serde_syn会尽力分配一个span,但使用serde必需的custom函数总是可能创建一个没有span的错误。

限制

serde_syn处于早期开发阶段,仍有一些问题。例如,如果尝试反序列化为serde_json Value,serde_syn会抛出错误,因为它还不支持自描述。

安装

在项目目录中运行以下Cargo命令:

cargo add serde_syn

或者在Cargo.toml中添加以下行:

serde_syn = "0.1.0"

1 回复

Rust序列化与反序列化库serde_syn的使用

serde_syn是一个强大的Rust库,它结合了serde的序列化能力和syn库的语法树解析功能,为处理Rust语法树提供了便利的工具。

主要功能

  1. 提供对Rust语法树(AST)的序列化和反序列化支持
  2. 允许将语法树转换为各种格式(如JSON)
  3. 支持代码生成和语法树操作

安装方法

在Cargo.toml中添加依赖:

[dependencies]
serde_syn = "0.1"
serde = { version = "1.0", features = ["derive"] }
syn = { version = "1.0", features = ["full", "extra-traits"] }

基本使用方法

1. 解析Rust代码为语法树

use serde_syn::syn;
use syn::parse_str;

fn main() {
    let code = r#"
        fn hello() {
            println!("Hello, world!");
        }
    "#;
    
    let ast: syn::ItemFn = parse_str(code).unwrap();
    println!("{:#?}", ast);
}

2. 序列化语法树为JSON

use serde_json::json;
use serde_syn::to_value;
use syn::parse_str;

fn main() {
    let code = "struct Point { x: i32, y: i32 }";
    let ast = parse_str::<syn::ItemStruct>(code).unwrap();
    
    let json_value = to_value(&ast).unwrap();
    println!("{}", serde_json::to_string_pretty(&json_value).unwrap());
}

3. 反序列化JSON为语法树

use serde_syn::from_value;
use serde_json::json;

fn main() {
    let json_data = json!({
        "struct": "Point",
        "fields": [
            {"name": "x", "ty": "i32"},
            {"name": "y", "ty": "i32"}
        ]
    });
    
    let ast: syn::ItemStruct = from_value(json_data).unwrap();
    println!("Generated struct: {}", quote::quote!(#ast));
}

高级用法

代码生成器示例

use serde_syn::{to_value, from_value};
use syn::{Ident, Type};
use serde_json::json;

fn generate_getter(struct_name: &str, field_name: &str, field_type: &str) -> String {
    let getter_name = format!("get_{}", field_name);
    
    let json_data = json!({
        "fn": {
            "name": getter_name,
            "inputs": [],
            "output": field_type,
            "block": {
                "stmt": [
                    {
                        "expr": {
                            "path": {
                                "segments": [
                                    {"ident": "self"},
                                    {"ident": field_name}
                                ]
                            }
                        }
                    }
                ]
            }
        }
    });
    
    let ast: syn::ItemFn = from_value(json_data).unwrap();
    quote::quote!(#ast).to_string()
}

fn main() {
    println!("{}", generate_getter("Point", "x", "i32"));
}

完整示例

下面是一个完整的serde_syn使用示例,展示了从解析代码到生成新代码的完整流程:

use serde_json::json;
use serde_syn::{from_value, to_value};
use syn::{parse_str, ItemFn, ItemStruct};
use quote::quote;

fn main() {
    // 示例1:解析Rust函数为语法树
    let function_code = r#"
        pub fn add(a: i32, b: i32) -> i32 {
            a + b
        }
    "#;
    
    let function_ast: ItemFn = parse_str(function_code).unwrap();
    println!("解析得到的函数语法树:\n{:#?}\n", function_ast);
    
    // 示例2:序列化结构体为JSON
    let struct_code = "pub struct User { name: String, age: u32 }";
    let struct_ast = parse_str::<ItemStruct>(struct_code).unwrap();
    
    let struct_json = to_value(&struct_ast).unwrap();
    println!("结构体序列化为JSON:\n{}\n", 
        serde_json::to_string_pretty(&struct_json).unwrap());
    
    // 示例3:从JSON生成Getter方法
    let json_input = json!({
        "struct": "User",
        "fields": [
            {"name": "name", "ty": "String"},
            {"name": "age", "ty": "u32"}
        ]
    });
    
    let user_struct: ItemStruct = from_value(json_input).unwrap();
    println!("从JSON生成的结构体:\n{}\n", quote!(#user_struct));
    
    // 为User结构体生成getter方法
    let getter_json = json!({
        "fn": {
            "vis": "pub",
            "name": "get_name",
            "inputs": [],
            "output": "&String",
            "block": {
                "stmt": [
                    {
                        "expr": {
                            "path": {
                                "segments": [
                                    {"ident": "self"},
                                    {"ident": "name"}
                                ]
                            }
                        }
                    }
                ]
            }
        }
    });
    
    let getter_ast: ItemFn = from_value(getter_json).unwrap();
    println!("生成的getter方法:\n{}", quote!(#getter_ast));
}

使用场景

  1. 代码分析工具:解析和检查Rust代码结构
  2. 代码生成器:根据模板或配置生成Rust代码
  3. 宏开发:在过程宏中处理语法树
  4. IDE插件:实现代码重构或分析功能

注意事项

  1. serde_syn仍在活跃开发中,API可能会有变化
  2. 处理大型语法树时注意性能问题
  3. 错误处理需要仔细,特别是解析用户提供的代码时

serde_syn为Rust元编程提供了强大的工具链,特别适合需要深度操作语法树的场景。

回到顶部