You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Rust结构体Schema反射,生成含类型与注释的JSON?

在Rust中实现结构体反射并生成带注释的JSON

Rust本身没有原生运行时反射,但可以通过过程宏实现这类需求,下面是两种通用解决方案:

方案一:自定义过程宏自动提取元数据

通过解析结构体的抽象语法树(AST),自动提取字段名、类型和文档注释,生成对应的JSON元数据。

实现步骤

  1. 定义带文档注释的目标结构体:
/// 关于接口的响应结构体
#[derive(Debug)]
struct AboutRsp {
    /// 服务名称
    service_name: String,
    /// 当前服务版本
    version: String,
    /// 服务启动时间戳
    start_time: u64,
}
  1. 编写过程宏解析AST:
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Fields};

#[proc_macro_derive(StructMetadata)]
pub fn derive_struct_metadata(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let struct_name = &input.ident;

    // 提取结构体的文档注释
    let struct_desc = input.attrs.iter()
        .filter_map(|attr| attr.parse_meta().ok())
        .filter(|meta| meta.path().is_ident("doc"))
        .filter_map(|meta| match meta {
            syn::Meta::NameValue(nv) => match nv.lit {
                syn::Lit::Str(s) => Some(s.value()),
                _ => None,
            },
            _ => None,
        })
        .collect::<Vec<_>>()
        .join("\n");

    // 处理结构体字段
    let fields = match input.data {
        syn::Data::Struct(s) => match s.fields {
            Fields::Named(fields) => fields.named,
            _ => panic!("仅支持具名字段的结构体"),
        },
        _ => panic!("仅支持结构体类型"),
    };

    let field_meta = fields.iter().map(|field| {
        let field_name = field.ident.as_ref().unwrap().to_string();
        let field_type = quote!(#field.ty).to_string();
        
        // 提取字段的文档注释
        let field_desc = field.attrs.iter()
            .filter_map(|attr| attr.parse_meta().ok())
            .filter(|meta| meta.path().is_ident("doc"))
            .filter_map(|meta| match meta {
                syn::Meta::NameValue(nv) => match nv.lit {
                    syn::Lit::Str(s) => Some(s.value()),
                    _ => None,
                },
                _ => None,
            })
            .collect::<Vec<_>>()
            .join("\n");

        quote! {
            serde_json::json!({
                "field_name": #field_name,
                "type": #field_type,
                "description": #field_desc
            })
        }
    });

    // 生成扩展代码
    let expanded = quote! {
        impl #struct_name {
            pub fn metadata() -> serde_json::Value {
                serde_json::json!({
                    "struct_name": stringify!(#struct_name),
                    "description": #struct_desc,
                    "fields": vec![#(#field_meta),*]
                })
            }
        }
    };

    expanded.into()
}
  1. 派生宏并生成JSON:
#[derive(StructMetadata)]
/// 关于接口的响应结构体
struct AboutRsp {
    /// 服务名称
    service_name: String,
    /// 当前服务版本
    version: String,
    /// 服务启动时间戳
    start_time: u64,
}

fn main() {
    let meta = AboutRsp::metadata();
    println!("{}", serde_json::to_string_pretty(&meta).unwrap());
}

运行后输出的JSON格式如下:

{
  "struct_name": "AboutRsp",
  "description": "关于接口的响应结构体",
  "fields": [
    {
      "field_name": "service_name",
      "type": "String",
      "description": "服务名称"
    },
    {
      "field_name": "version",
      "type": "String",
      "description": "当前服务版本"
    },
    {
      "field_name": "start_time",
      "type": "u64",
      "description": "服务启动时间戳"
    }
  ]
}

方案二:手动绑定元数据(轻量场景)

如果不想编写复杂宏,可以手动维护结构体和对应的元数据结构,适合小型项目:

use serde::Serialize;

/// 关于接口的响应结构体
#[derive(Debug, Serialize)]
struct AboutRsp {
    service_name: String,
    version: String,
    start_time: u64,
}

#[derive(Serialize)]
struct FieldMeta {
    field_name: &'static str,
    r#type: &'static str,
    description: &'static str,
}

#[derive(Serialize)]
struct StructMeta {
    struct_name: &'static str,
    description: &'static str,
    fields: Vec<FieldMeta>,
}

impl AboutRsp {
    pub fn metadata() -> StructMeta {
        StructMeta {
            struct_name: "AboutRsp",
            description: "关于接口的响应结构体",
            fields: vec![
                FieldMeta {
                    field_name: "service_name",
                    r#type: "String",
                    description: "服务名称",
                },
                FieldMeta {
                    field_name: "version",
                    r#type: "String",
                    description: "当前服务版本",
                },
                FieldMeta {
                    field_name: "start_time",
                    r#type: "u64",
                    description: "服务启动时间戳",
                },
            ],
        }
    }
}

fn main() {
    let meta = AboutRsp::metadata();
    println!("{}", serde_json::to_string_pretty(&meta).unwrap());
}

核心说明

  • 过程宏是实现自动提取的关键,能在编译阶段解析结构体的所有静态信息
  • 文档注释在AST中以#[doc = "..."]属性存在,需过滤提取字符串内容
  • 类型字符串可通过quote!将类型Token转换为文本,复杂类型可借助syn的类型打印功能优化

内容的提问来源于stack exchange,提问作者Jamie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 06:03:37