让 prost 生成的 Rust 代码支持 Serialize/Deserialize 到 JSON
Telegram Bot API 返回的都是 JSON 数据,比如 访问 https://api.telegram.org/bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11/getMe 返回一个 User
我想把这个 JSON 转换成 Rust 中的结构,方便后续操作,这个可以利用 serde 很方便地做到
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 1, y: 2 };
// Convert the Point to a JSON string.
let serialized = serde_json::to_string(&point).unwrap();
// Prints serialized = {"x":1,"y":2}
println!("serialized = {}", serialized);
// Convert the JSON string back to a Point.
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
// Prints deserialized = Point { x: 1, y: 2 }
println!("deserialized = {:?}", deserialized);
}
问题是手动定义一个个 struct 太麻烦,那么可以用 macro 来帮我生成这些长得差不多的 struct 代码,但是仍然需要以另一种格式先定义好这些 JSON,比如:
{
"struct": "User",
"fields": {
"id": {
"type": "integer64",
"optional": false
},
"is_bot": {
"type": "boolean",
"optional": false
},
"first_name": {
"type": "string",
"optional": false
},
"last_name": {
"type": "string",
"optional": true
}
}
}
然后通过解析这段 JSON,再通过 macro 生成我想要的各个 struct。问题是写这么一份 JSON 仍然很麻烦。有没有一种更直观的写法?
有,protobuf !
telegram.proto
syntax = "proto3";
package telegram;
message User {
int64 id = 1;
bool is_bot = 2;
string first_name = 3;
optional string last_name = 4;
optional string username = 5;
optional string language_code = 6;
}
相当直观了
现在只需要让 protobuf 生成的代码,可以支持 serde::Serialize 与 serde::Deserialize 即可
搜索一下 prost-build 有没有相应的支持
Config 里有个 type_attribute 方法恰好可以满足需求
最终相关代码大概如此:
build.rs
use std::io::Result;
fn main() -> Result<()> {
let mut config = prost_build::Config::new();
config.out_dir("src/proto");
config.type_attribute(".", "#[derive(serde::Serialize,serde::Deserialize)]");
config.compile_protos(&["src/proto/telegram.proto"], &["src/proto"])?;
Ok(())
}
对应生成的代码:
telegram.rs
#[derive(serde::Serialize,serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct User {
#[prost(int64, tag="1")]
pub id: i64,
#[prost(bool, tag="2")]
pub is_bot: bool,
#[prost(string, tag="3")]
pub first_name: ::prost::alloc::string::String,
#[prost(string, optional, tag="4")]
pub last_name: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag="5")]
pub username: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag="6")]
pub language_code: ::core::option::Option<::prost::alloc::string::String>,
}
写个测试看下效果:
#[cfg(test)]
mod tests {
use super::*;
mod telegram {
include!("proto/telegram.rs");
}
#[test]
fn it_works() {
let u = telegram::User::default();
let s = serde_json::to_string(&u).unwrap();
let u: telegram::User = serde_json::from_str(&s).unwrap();
println!("s: {}", s);
println!("u: {:#?}", u);
}
}
输出:
s: {"id":0,"is_bot":false,"first_name":"","last_name":null,"username":null,"language_code":null}
u: User {
id: 0,
is_bot: false,
first_name: "",
last_name: None,
username: None,
language_code: None,
}
更进一步,可以在 build.rs 中直接抓取 https://core.telegram.org/bots/api 页面,然后解析其内容,转换为 protobuf,再生成 Rust 的 struct,一气呵成
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2021-07-09 华为路由器端口映射