14个版本
0.6.0 | 2024年7月29日 |
---|---|
0.5.1 | 2024年4月13日 |
0.5.0 | 2023年10月9日 |
0.4.2 | 2023年5月22日 |
0.3.0 | 2021年11月26日 |
#306 在 编码
128,429 每月下载量
被 35 个crate使用 (19直接)
98KB
1.5K SLoC
PROST Well Known Types JSON序列化和反序列化
Prost 是一个为 Protocol Buffers 的 Rust语言 实现,它从proto2
和proto3
文件生成简单、惯用的Rust代码。
它包括prost-types
,这提供了对protobuf Well-Known-Types (WKT)的基本支持,但支持是基本的。例如,它不包括对Any
类型消息的打包和拆包,也没有在该类型上提供大量的JSON序列化和反序列化支持。
如果您需要以下内容,这个crate可以帮助您
要使用它,请将此crate与prost一起包含
[dependencies]
prost = "0.13"
prost-wkt = "0.6"
prost-wkt-types = "0.6"
serde = { version = "1.0", features = ["derive"] }
[build-dependencies]
prost-build = "0.13"
prost-wkt-build = "0.6"
在您的build.rs
中,请确保添加以下选项
use std::{env, path::PathBuf};
use prost_wkt_build::*;
fn main() {
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
let descriptor_file = out.join("descriptors.bin");
let mut prost_build = prost_build::Config::new();
prost_build
.type_attribute(
".",
"#[derive(serde::Serialize,serde::Deserialize)]"
)
.extern_path(
".google.protobuf.Any",
"::prost_wkt_types::Any"
)
.extern_path(
".google.protobuf.Timestamp",
"::prost_wkt_types::Timestamp"
)
.extern_path(
".google.protobuf.Value",
"::prost_wkt_types::Value"
)
.file_descriptor_set_path(&descriptor_file)
.compile_protos(
&[
"proto/messages.proto"
],
&["proto/"],
)
.unwrap();
let descriptor_bytes =
std::fs::read(descriptor_file)
.unwrap();
let descriptor =
FileDescriptorSet::decode(&descriptor_bytes[..])
.unwrap();
prost_wkt_build::add_serde(out, descriptor);
}
上述配置将包含在每个生成的结构体上的Serialize
和Deserialize
。这将允许您完全使用serde
。此外,它还确保了Any
类型以JSON形式正确反序列化。例如,假设我们在proto文件中定义了以下消息
syntax = "proto3";
import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
package my.pkg;
message Request {
string requestId = 1;
google.protobuf.Any payload = 2;
}
message Foo {
string data = 1;
google.protobuf.Timestamp timestamp = 2;
}
使用上述配置通过prost-build
生成上述消息的Rust结构体后,您将能够进行以下操作
use serde::{Deserialize, Serialize};
use chrono::prelude::*;
use prost_wkt_types::*;
include!(concat!(env!("OUT_DIR"), "/my.pkg.rs"));
fn main() -> Result<(), AnyError> {
let foo_msg: Foo = Foo {
data: "Hello World".to_string(),
timestamp: Some(Utc::now().into()),
};
let mut request: Request = Request::default();
let any = Any::try_pack(foo_msg)?;
request.request_id = "test1".to_string();
request.payload = Some(any);
let json = serde_json::to_string_pretty(&request).expect("Failed to serialize request");
println!("JSON:\n{}", json);
let back: Request = serde_json::from_str(&json).expect("Failed to deserialize request");
if let Some(payload) = back.payload {
let unpacked: Box< dyn MessageSerde> = payload.try_unpack()?;
let unpacked_foo: &Foo = unpacked
.downcast_ref::<Foo>()
.expect("Failed to downcast payload to Foo");
println!("Unpacked: {:?}", unpacked_foo);
}
}
上述操作将生成以下stdout
JSON:
{
"requestId": "test1",
"payload": {
"@type": "type.googleapis.com/my.pkg.Foo",
"data": "Hello World",
"timestamp": "2020-05-25T12:19:57.755998Z"
}
}
Unpacked: Foo { data: "Hello World", timestamp: Some(Timestamp { seconds: 1590409197, nanos: 755998000 }) }
请注意,请求消息已按protobuf规范正确序列化为JSON,并且也可以进行反序列化。
请参阅example
子项目以获取一个完全功能的示例。
已知问题
oneOf类型
prost-build生成oneOf
类型的方法是将它放在一个子模块中,例如
message SomeOne {
oneof body {
string some_string = 1;
bool some_bool = 2;
float some_float = 3;
}
}
转换为Rust代码如下
#[derive(Serialize, Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[prost(package="my.pkg")]
pub struct SomeOne {
#[prost(oneof="some_one::Body", tags="1, 2, 3")]
pub body: ::core::option::Option<some_one::Body>,
}
/// Nested message and enum types in `SomeOne`.
pub mod some_one {
#[derive(Serialize, Deserialize)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Body {
#[prost(string, tag="1")]
SomeString(::prost::alloc::string::String),
#[prost(bool, tag="2")]
SomeBool(bool),
#[prost(float, tag="3")]
SomeFloat(f32),
}
}
然而,Rust需要在每个模块中导入宏,因此每个模块都应该添加以下内容
use serde::{Serialize, Deserialize};
在生成的代码片段中,上述语句在some_one
模块中缺失,Rust编译器会对此提出警告。为了修复它,我们不得不在some_one
模块中添加适当的use语句,如下所示
#[derive(Serialize, Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[prost(package="my.pkg")]
pub struct SomeOne {
#[prost(oneof="some_one::Body", tags="1, 2, 3")]
pub body: ::core::option::Option<some_one::Body>,
}
/// Nested message and enum types in `SomeOne`.
pub mod some_one {
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Body {
#[prost(string, tag="1")]
SomeString(::prost::alloc::string::String),
#[prost(bool, tag="2")]
SomeBool(bool),
#[prost(float, tag="3")]
SomeFloat(f32),
}
}
幸运的是,你可以通过调整build.rs
来实现上述功能。例如,以下配置将根据需要将所需的serde导入添加到some_one
模块中
fn main() {
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
let descriptor_file = out.join("descriptors.bin");
let mut prost_build = prost_build::Config::new();
prost_build
.type_attribute(
".my.pkg.MyEnum",
"#[derive(serde::Serialize,serde::Deserialize)]"
)
.type_attribute(
".my.pkg.MyMessage",
"#[derive(serde::Serialize,serde::Deserialize)] #[serde(default)]"
)
.type_attribute(
".my.pkg.SomeOne.body",
"#[derive(serde::Serialize,serde::Deserialize)]"
)
.extern_path(
".google.protobuf.Any",
"::prost_wkt_types::Any"
)
.extern_path(
".google.protobuf.Timestamp",
"::prost_wkt_types::Timestamp"
)
.extern_path(
".google.protobuf.Value",
"::prost_wkt_types::Value"
)
.file_descriptor_set_path(&descriptor_file)
.compile_protos(
&[
"proto/messages.proto"
],
&["proto/"],
)
.unwrap();
let descriptor_bytes =
std::fs::read(descriptor_file).unwrap();
let descriptor =
FileDescriptorSet::decode(&descriptor_bytes[..]).unwrap();
prost_wkt_build::add_serde(out, descriptor);
}
开发
欢迎贡献!
升级Prost
在升级Prost到最新版本时,请确保将prost-types
的最新更改合并到prost-wkt-types
中,以确保完全兼容。
目前,直到Prost中的实现完全稳定,Name
特质尚未特别实现。
MSRV
最低支持的Rust版本是Rust 1.70.0。
许可证
prost-wkt
根据Apache许可证(版本2.0)的条款进行分发。
有关详细信息,请参阅LICENSE。
版权所有 2023 Ferdinand de Antoni
依赖项
~2.2–6MB
~106K SLoC