Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serializing a Message to Any #299

Open
KaiserKarel opened this issue Apr 5, 2020 · 22 comments
Open

Serializing a Message to Any #299

KaiserKarel opened this issue Apr 5, 2020 · 22 comments

Comments

@KaiserKarel
Copy link

I'm unable to find any docs on how to create an Any from a Message. Do I need to serialize the message myself and set the typeURL?

@fridge-dev
Copy link

+1, I need to deserialize an Any to a known type. In other languages, I see there are pack/unpack methods. I couldn't find such methods on the Any type (https://docs.rs/prost-types/0.6.1/src/prost_types/protobuf.rs.html#964-998 )

@fdeantoni
Copy link

Similar to #277.

@cetanu
Copy link

cetanu commented Sep 8, 2020

Subscribed... I know how to go from a type to Any, but not the other way around.

@petergarnaes
Copy link

The tools for de-serializing an Any type is already there, you can match on the type URL, and decode the value field using Message::decode.

With a little bit of macro magic you don't have to write a lot of code, I have made an example repo. IMO this is simpler and can easily be adapted to your own needs.

@vbkaisetsu
Copy link

If there is a mechanism for Message to return type_url, it can support generics.

use prost::Message;
use prost_types::Any;

fn to_any<T>(message: &T) -> Any where T: Message {
    Any {
        type_url: T::type_url().to_string(),
        value: message.encode_to_vec(),
    }
}

fn from_any<T>(message: &Any) -> Result<T, DecodeError> where T: Message + Default {
    if &message.type_url == T::type_url() {
        T::decode(message.value.as_slice())
    } else {
        Err(DecodeError::new("Invalid type_url"))
    }
}

@colin-grapl
Copy link

Adding a type_url to Message would definitely help. After that it would be straightforward to add pack/unpack methods for messages.

@ghost
Copy link

ghost commented Apr 11, 2022

Create a datafusion-proto crate for datafusion protobuf serialization apache/datafusion#1887

@vbkaisetsu is there a type_url for prost::Message? I don't think there is one if not this will be straightforward

@vbkaisetsu
Copy link

vbkaisetsu commented Apr 11, 2022

@nyanchor I previously created #535 to support seamless seriarization, but it needs further discussion.

@LucioFranco
Copy link
Member

Yeah, I think supporting this is a good idea. Will need to dig in a bit more to understand how other languages support this feature.

tony-iqlusion added a commit to cosmos/cosmos-rust that referenced this issue Jul 25, 2022
When trying to define an `osmosis-proto` crate (#239), we ran into the
problem that it needed to import `cosmrs` to be able to impl the
`MsgProto` trait.

We couldn't follow the same pattern as `cosmrs` defining the type URLs
for `cosmos-sdk-proto`, which it could only do because it defined the
`MsgProto` trait as well.

Knowledge of the type URLs is necessary to convert to/from `Any`, which
Cosmos SDK uses all over the place.

So far there isn't a good upstream solution to this problem in `prost`
or AFAICT in `tendermint-proto` either. There's an upstream tracking
issue for `prost` here:

tokio-rs/prost#299

The ideal solution to this problem seems to be adding a `TYPE_URL` to
`prost::Message`, and automatically populating them with `prost-build`.

Failing that, this commit introduces a `TypeUrl` trait with an
associated `TYPE_URL` const (previously provided by the `MsgProto`
trait).

The `from_any` and `to_any` methods have been moved to `MessageExt`.
tony-iqlusion added a commit to cosmos/cosmos-rust that referenced this issue Jul 25, 2022
When trying to define an `osmosis-proto` crate (#239), we ran into the
problem that it needed to import `cosmrs` to be able to impl the
`MsgProto` trait.

We couldn't follow the same pattern as `cosmrs` defining the type URLs
for `cosmos-sdk-proto`, which it could only do because it defined the
`MsgProto` trait as well.

Knowledge of the type URLs is necessary to convert to/from `Any`, which
Cosmos SDK uses all over the place.

So far there isn't a good upstream solution to this problem in `prost`
or AFAICT in `tendermint-proto` either. There's an upstream tracking
issue for `prost` here:

tokio-rs/prost#299

The ideal solution to this problem seems to be adding a `TYPE_URL` to
`prost::Message`, and automatically populating them with `prost-build`.

Failing that, this commit introduces a `TypeUrl` trait with an
associated `TYPE_URL` const (previously provided by the `MsgProto`
trait).

The `from_any` and `to_any` methods have been moved to `MessageExt`.
@tarcieri
Copy link
Contributor

@LucioFranco the simplest way to support it is adding an associated const TYPE_URL: &'static str to either the Message trait (or another specialized trait if adding it to Message is too onerous). Or if there are genuinely good use cases, possibly a static method.

Then prost-build just needs to compute and generate it for each type.

tony-iqlusion added a commit to cosmos/cosmos-rust that referenced this issue Jul 25, 2022
When trying to define an `osmosis-proto` crate (#239), we ran into the
problem that it needed to import `cosmrs` to be able to impl the
`MsgProto` trait.

We couldn't follow the same pattern as `cosmrs` defining the type URLs
for `cosmos-sdk-proto`, which it could only do because it defined the
`MsgProto` trait as well.

Knowledge of the type URLs is necessary to convert to/from `Any`, which
Cosmos SDK uses all over the place.

So far there isn't a good upstream solution to this problem in `prost`
or AFAICT in `tendermint-proto` either. There's an upstream tracking
issue for `prost` here:

tokio-rs/prost#299

The ideal solution to this problem seems to be adding a `TYPE_URL` to
`prost::Message`, and automatically populating them with `prost-build`.

Failing that, this commit introduces a `TypeUrl` trait with an
associated `TYPE_URL` const (previously provided by the `MsgProto`
trait).

The `from_any` and `to_any` methods have been moved to `MessageExt`.
@LucioFranco
Copy link
Member

@tarcieri yes, though I don't remember (or couldn't find but didn't look super hard) if there was actually a concrete way to generate that url that isn't google specific etc.

@tarcieri
Copy link
Contributor

To my knowledge the type URLs take the form:

/<package>.<type name>

e.g.

/foo.bar.baz.MyTypeName

@kurojishi
Copy link

Bumping this one up as it's definitely something that will make my life easier.

@Percivalll
Copy link

Do we have any latest process?

@tarcieri
Copy link
Contributor

tarcieri commented May 15, 2023

One way to get started would be to add a trait with a carrier for the type URL:

pub trait TypeUrl: Message {
    const TYPE_URL: &'static str;
}

This would avoid the need to immediately populate it for every Message, but provide a common way to implement methods (e.g. on prost_types::Any) for decoding/encoding Message + TypeUrl types from/to Any.

At least initially, the TypeUrl annotations could be added manually, until such a time that prost-build can emit them automatically.

Edit: opened a PR with this idea: #858

tarcieri added a commit to tarcieri/prost that referenced this issue May 17, 2023
As discussed in tokio-rs#299, adds a `TypeUrl` trait which associates a type URL
constant with a `Message` type.

This enables adding methods to `Any` for decoding/encoding messages:

- `Any::from_message`: encodes a given `Message`, returning `Any`.
- `Any::to_message`: decodes `Any::value` as the given `Message`, first
  validating the message type has the expected type URL.
tarcieri added a commit to tarcieri/prost that referenced this issue May 17, 2023
As discussed in tokio-rs#299, adds a `TypeUrl` trait which associates a type URL
constant with a `Message` type.

This enables adding methods to `Any` for decoding/encoding messages:

- `Any::from_message`: encodes a given `Message`, returning `Any`.
- `Any::to_message`: decodes `Any::value` as the given `Message`, first
  validating the message type has the expected type URL.
tarcieri added a commit to tarcieri/prost that referenced this issue May 17, 2023
As discussed in tokio-rs#299, adds a `TypeUrl` trait which associates a type URL
constant with a `Message` type.

This enables adding methods to `Any` for decoding/encoding messages:

- `Any::from_message`: encodes a given `Message`, returning `Any`.
- `Any::to_message`: decodes `Any::value` as the given `Message`, first
  validating the message type has the expected type URL.
tarcieri added a commit to tarcieri/prost that referenced this issue May 17, 2023
As discussed in tokio-rs#299, adds a `TypeUrl` trait which associates a type URL
constant with a `Message` type.

This enables adding methods to `Any` for decoding/encoding messages:

- `Any::from_message`: encodes a given `Message`, returning `Any`.
- `Any::to_message`: decodes `Any::value` as the given `Message`, first
  validating the message type has the expected type URL.
tarcieri added a commit to tarcieri/prost that referenced this issue May 17, 2023
As discussed in tokio-rs#299, adds a `TypeUrl` trait which associates a type URL
constant with a `Message` type.

This enables adding methods to `Any` for decoding/encoding messages:

- `Any::from_message`: encodes a given `Message`, returning `Any`.
- `Any::to_message`: decodes `Any::value` as the given `Message`, first
  validating the message type has the expected type URL.
tarcieri added a commit to tarcieri/prost that referenced this issue Aug 14, 2023
As discussed in tokio-rs#299 and tokio-rs#858, adds a `Name` trait which associates a
type name and package constants with a `Message` type.
It also provides `full_name` and `type_url` methods.

The `type_url` method is used by newly added methods on the `Any` type
which can be used for decoding/encoding messages:

- `Any::from_msg`: encodes a given `Message`, returning `Any`.
- `Any::to_msg`: decodes `Any::value` as the given `Message`, first
  validating the message type has the expected type URL.
@tarcieri
Copy link
Contributor

I opened #896 as my latest attempt to implement this feature

LucioFranco added a commit that referenced this issue Sep 1, 2023
* feat: `Name` trait + `Any` encoding support

As discussed in #299 and #858, adds a `Name` trait which associates a
type name and package constants with a `Message` type.
It also provides `full_name` and `type_url` methods.

The `type_url` method is used by newly added methods on the `Any` type
which can be used for decoding/encoding messages:

- `Any::from_msg`: encodes a given `Message`, returning `Any`.
- `Any::to_msg`: decodes `Any::value` as the given `Message`, first
  validating the message type has the expected type URL.

* Add private `TypeUrl` type

Implements the basic rules for parsing type URLs as documented in:

https://github.com/protocolbuffers/protobuf/blob/a281c13/src/google/protobuf/any.proto#L129C2-L156C50

Notably this extracts the final path segment of the URL which contains
the full name of the type, and uses that for type comparisons.

* CI: bump test toolchain to 1.64

This is the MSRV of `petgraph` now:

error: package `petgraph v0.6.4` cannot be built because it requires rustc 1.64 or newer, while the currently active rustc version is 1.63.0

* Add `Name` impls for well-known protobuf types

Also adds tests for `Any::{from_msg, to_msg}`.

* Fix no_std

---------

Co-authored-by: Lucio Franco <luciofranco14@gmail.com>
@journaux
Copy link

journaux commented Sep 22, 2023

@tarcieri would you mind indicating why Name + Pkg needed in your implementation ? also, would you mind indicating why type_url() isn't exported by default on Prost::Message ?

I can implement prost-build; however, a better approach would remove those unnecessary traits

fn macros(fds: FileDescriptorSet) {
  for fd in &fds.file {
   .....
    for buf in &fd.message_type {
      ....
      let tokens = quote! {
          impl prost::Name for #bufname {
              const NAME: &'static str = #bufurl;
              const PACKAGE: &'static str = #pkg;
              fn type_url() -> String {
                  #bufurl.to_string()
              }
          }
      };
    }
  }
}

@tarcieri
Copy link
Contributor

would you mind indicating why Name + Pkg needed in your implementation

Please see the discussion here: #858 (comment)

also, would you mind indicating why type_url() isn't exported by default on Prost::Message

There is currently no prost-build support, so trying to do that would break all code generation in addition to all currently generated code, as it would be a breaking change to Message.

In the future when there is prost-build support, it might make sense to combine Name into Message.

@journaux
Copy link

so it’s a problem with the design + impl not any technical decision / desire ?

If that’s the case — prost::Message::type_url can trivially be added to prost-build

@tarcieri
Copy link
Contributor

It’s something that could potentially be merged into Message in the next breaking release provided prost-build support were implemented, yes

@andrewhickman
Copy link
Contributor

andrewhickman commented Nov 3, 2023

Merging Name into Message might be problematic for custom Message implementations like prost_reflect::DynamicMessage, unless the string types were changed to something like Cow<'static, str> to allow an owned string.

@tarcieri
Copy link
Contributor

tarcieri commented Nov 3, 2023

Yeah, the Name trait in its current form is poorly suited to anything dynamic. We could potentially refactor it to make it easier to use for that use case (and potentially trait object safe) in the next release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests