#json #serialization #deserialize #prost #protobuf #wkt #type

prost-wkt

prost的辅助crate,允许对已知类型进行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日

#128编码

Download history 20993/week @ 2024-04-18 18733/week @ 2024-04-25 16079/week @ 2024-05-02 17135/week @ 2024-05-09 31645/week @ 2024-05-16 31054/week @ 2024-05-23 18460/week @ 2024-05-30 33816/week @ 2024-06-06 26984/week @ 2024-06-13 29385/week @ 2024-06-20 32578/week @ 2024-06-27 24147/week @ 2024-07-04 26813/week @ 2024-07-11 23725/week @ 2024-07-18 23208/week @ 2024-07-25 31333/week @ 2024-08-01

110,999 每月下载量
36 个crate中使用了(直接使用 11 个)

Apache-2.0

16KB

PROST已知类型JSON序列化和反序列化

crates.io build

Prost 是一个为 Protocol BuffersRust语言 实现,它可以从 proto2proto3 文件生成简单、惯用的Rust代码。

它包括 prost-types,提供了对protobuf已知类型(WKT)的基本支持,但支持较为基础。例如,它不包含在 Any 类型中的消息打包和解包,也没有为该类型的JSON序列化和反序列化提供太多支持。

如果您需要以下功能,则此crate可以帮助您

  • 将消息打包到或从 Any 中打包和解包的辅助方法
  • chrono 类型转换为 Timestamp 并返回的辅助方法
  • 将常见rust类型转换为 Value 并返回的辅助方法
  • 对上述类型的serde支持。

要使用它,请将此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);
}

上述配置将在每个生成的结构体上包含 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中,以确保完全兼容。

目前,直到Prost的实现完全稳定之前,尚未特别实现Name特质。

MSRV

最低支持的Rust版本是Rust 1.70.0。

许可

prost-wkt在Apache License(版本2.0)的条款下分发。

有关详细信息,请参阅LICENSE

版权所有 2023 Ferdinand de Antoni

依赖项

~2.2–3.5MB
~65K SLoC