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 日 |
#790 在 编码
118,269 每月下载量
在 38 个crate中使用 (10 直接使用)
16KB
82 行
PROST Well Known Types JSON 序列化和反序列化
Prost 是一个用于 Protocol Buffers 的 Rust 语言 实现,它从 proto2
和 proto3
文件生成简单、惯用的 Rust 代码。
它包括 prost-types
,提供了对 protobuf Well-Known-Types (WKT) 的基本支持,但支持是基本的。例如,它不包括在 Any
类型中的消息打包和解包,也没有为该类型的 JSON 序列化和反序列化提供很多支持。
如果您需要以下功能,这个crate可以帮到您
- 打包和解包消息到/从 Any 的辅助方法
- 将 chrono 类型转换为 Timestamp 并返回的辅助方法
- 将常见的 Rust 类型转换为 Value 并返回的辅助方法
- 对上述类型提供 serde 支持
要使用它,请与 prost 一起包含此 crate
[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
中,以确保完全兼容。
目前,Name
特质尚未实现,直到Prost的实现完全稳定。
MSRV
最低支持的Rust版本是Rust 1.70.0。
许可
prost-wkt
在Apache License(版本2.0)的条款下分发。
有关详细信息,请参阅LICENSE。
版权所有 2023 Ferdinand de Antoni
依赖项
~7–18MB
~227K SLoC