#json #serialization #deserialize #prost #buffer #wkt #type

build prost-wkt-build

prost 的辅助库,允许 Well Known Types (WKT) 进行 JSON 序列化和反序列化

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编码

Download history 21590/week @ 2024-04-22 20190/week @ 2024-04-29 19392/week @ 2024-05-06 22337/week @ 2024-05-13 40068/week @ 2024-05-20 21454/week @ 2024-05-27 22402/week @ 2024-06-03 39274/week @ 2024-06-10 25066/week @ 2024-06-17 33773/week @ 2024-06-24 27609/week @ 2024-07-01 34414/week @ 2024-07-08 26278/week @ 2024-07-15 28894/week @ 2024-07-22 28257/week @ 2024-07-29 33798/week @ 2024-08-05

118,269 每月下载量
38 个crate中使用 (10 直接使用)

Apache-2.0

16KB
82

PROST Well Known Types JSON 序列化和反序列化

crates.io build

Prost 是一个用于 Protocol BuffersRust 语言 实现,它从 proto2proto3 文件生成简单、惯用的 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);
}

上述配置将包括为每个生成的结构体添加 SerializeDeserialize。这将允许您完全使用 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