stepancheg / rust-protobuf

Rust implementation of Google protocol buffers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[custom serde annotations] the trait `Deserialize<'_>` is not implemented for `MessageField<T>`

svanharmelen opened this issue · comments

I also just opened #702 as well as my goal is to parse both protobuf and JSON payloads using the same generated structs using either protobuf-json-mapping or custom Serde annotations. The other issue describes an issue with protobuf-json-mapping and this issue is used to describe an issue with using custom Serde annotations.

To make it easy to reproduce the issue, I created a debug-example repo with a "working" example giving the error: https://github.com/svanharmelen/debug-example/tree/serde (notice this example is in the serde branch)

I'm using an update proto file:

syntax = "proto2";

message TestMessage {
  message SubMessage {
    optional string name = 1;
  }

  message MainMessage {
    optional string name           = 1;
    optional SubMessage submessage = 2;

    oneof value {
      uint32   int_value    = 3;
      uint64   long_value   = 4;
      float    float_value  = 5;
      double   double_value = 6;
    }
  }
}

But this time I am using this lib.rs:

include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));

#[cfg(test)]
mod test {
    use crate::example::test_message::{main_message::Value, MainMessage};

    #[test]
    fn test() {
        let json = r#"{
          "name": "test",
          "value": 10.3
        }"#;

        let message: MainMessage = serde_json::from_str(json).expect("failed to parse JSON");

        assert_eq!(message.name, Some("test".to_string()));
        assert_eq!(message.value, Some(Value::FloatValue(10.3)));
    }
}

The error I get is about MessageField<T> types not implementing the Deserialize trait and I have now idea how I should fix that in a generic way (the actual proto file I use has a bunch of these embedded submessages):

3897 |         pub properties: ::protobuf::MessageField<PropertySet>,
     |         ^^^ the trait `Deserialize<'_>` is not implemented for `MessageField<PropertySet>`

I'm fine to use either Serde annotations or protobuf-json-mapping, whichever one is easier to fix will do I guess 😉

Thanks!

I think some serde annotation can be inserted to specify message field should be parsed/serialized with custom function.

Yes, I was thinking the same... But I cannot find a way to "detect" which field are of type MessageField<T> in the available CustomizeCallback methods in order to write an annotation on them. Any suggestions?

@svanharmelen I'm not sure if you ever figured this out, but I have some solution, although I am using it to skip adding derives on MessageField.

The fn message bit adds the SerDe derive above the outer message type, then I skip on inner MessageField, and special fields. Apparently you can use field.proto().type_() to check the type of field.

struct GenSerde;
impl CustomizeCallback for GenSerde {
  fn message(&self, _message: &MessageDescriptor) -> Customize {
    Customize::default().before("#[derive(::serde::Serialize, ::serde::Deserialize)]")
  }

  // Skips adding the derive macro above field-level Message types, as they
  // are wrapped in a MessageField struct which is not Serialize or Deserialize
  fn field(&self, field: &FieldDescriptor) -> Customize {
    if field.proto().type_() == Type::TYPE_MESSAGE {
      Customize::default().before("#[serde(skip)]")
    } else {
      Customize::default()
    }
  }

  fn special_field(&self, _message: &MessageDescriptor, _field: &str) -> Customize {
    Customize::default().before("#[serde(skip)]")
  }
}

Thanks for your reply @icdevin, much appreciated!

I ended up making it very specific by using the field name. Feels a bit nasty, but it does solve the issue 😏 If I remember correctly I did try your suggestion, but that ended up skipping more field then needed. Might check it again when I have to update or make changes to the crate again...