Как использовать (упаковать) Google.Protobuf.WellknownTypes.Any в коде protobuf-net сначала gRPC
Я создаю службу gRPC, и мы решили выбрать сначала код с помощью protobuf-net. Теперь я столкнулся со сценарием, в котором у нас есть пара классов, которые нужно обернуть. Мы не хотим определять KnownTypes в классе MyMessage (просто пример имени, чтобы проиллюстрировать проблему). Поэтому я пытаюсь использовать тип Any, который в настоящее время вызывает у меня проблемы с упаковкой.
В образце кода есть MyMessage, который определяет некоторые значения заголовков и может доставлять любой тип в качестве полезной нагрузки.
[ProtoContract]
public class MyMessage
{
[ProtoMember(1)] public int HeaderValue1 { get; set; }
[ProtoMember(2)] public string HeaderValue2 { get; set; }
[ProtoMember(3)] public Google.Protobuf.WellknownTypes.Any Payload { get; set; }
}
[ProtoContract]
public class Payload1
{
[ProtoMember(1)] public bool Data1 { get; set; }
[ProtoMember(2)] public string Data2 { get; set; }
}
[ProtoContract]
public class Payload2
{
[ProtoMember(1)] public string Data1 { get; set; }
[ProtoMember(2)] public string Data2 { get; set; }
}
Где-то в коде я создаю свое сообщение с полезной нагрузкой ...
Payload2 payload = new Payload2 {
Data1 = "abc",
Data2 = "def"
};
MyMessage msg = new MyMessage
{
HeaderValue1 = 123,
HeaderValue2 = "iAmHeaderValue2",
Payload = Google.Protobuf.WellknownTypes.Any.Pack(payload)
};
Что не работает, потому что
Payload1
а также
Payload2
необходимо реализовать
Google.Protobuf.IMessage
. Поскольку я не могу понять, как это сделать, и не могу найти много информации, как это сделать, мне интересно, не ошибаюсь ли я.
- Как предполагается использовать Any в protobuf-net?
- Есть ли простой (но совместимый) способ упаковать первый класс кода C # в Google.Protobuf.WellknownTypes.Any?
- Мне действительно нужно внедрять Google.Protobuf.IMessage?
2 ответа
Я написал эту хакерскую реализацию Any.
[ProtoContract(Name = "type.googleapis.com/google.protobuf.Any")]
public class Any
{
/// <summary>Pack <paramref name="value"/></summary>
public static Any Pack(object? value)
{
// Handle null
if (value == null) return new Any { TypeUrl = null!, Value = Array.Empty<byte>() };
// Get type
System.Type type = value.GetType();
// Write here
MemoryStream ms = new MemoryStream();
// Serialize
RuntimeTypeModel.Default.Serialize(ms, value);
// Create any
Any any = new Any
{
TypeUrl = $"{type.Assembly.GetName().Name}/{type.FullName}",
Value = ms.ToArray()
};
// Return
return any;
}
/// <summary>Unpack any record</summary>
public object? Unpack()
{
// Handle null
if (TypeUrl == null || Value == null || Value.Length == 0) return null;
// Find '/'
int slashIx = TypeUrl.IndexOf('/');
// Convert to C# type name
string typename = slashIx >= 0 ? $"{TypeUrl.Substring(slashIx + 1)}, {TypeUrl.Substring(0, slashIx)}" : TypeUrl;
// Get type (Note security issue here!)
System.Type type = System.Type.GetType(typename, true)!;
// Deserialize
object value = RuntimeTypeModel.Default.Deserialize(type, Value.AsMemory());
// Return
return value;
}
/// <summary>Test type</summary>
public bool Is(System.Type type) => $"{type.Assembly.GetName().Name}/{type.FullName}" == TypeUrl;
/// <summary>Type url (using C# type names)</summary>
[ProtoMember(1)]
public string TypeUrl = null!;
/// <summary>Data serialization</summary>
[ProtoMember(2)]
public byte[] Value = null!;
/// <summary></summary>
public static implicit operator Container(Any value) => new Container(value.Unpack()! );
/// <summary></summary>
public static implicit operator Any(Container value) => Any.Pack(value.Value);
/// <summary></summary>
public struct Container
{
/// <summary></summary>
public object? Value;
/// <summary></summary>
public Container()
{
this.Value = null;
}
/// <summary></summary>
public Container(object? value)
{
this.Value = value;
}
}
}
«System.Object» можно использовать как поле или свойство другой записи со следующим трюком:
[DataContract]
public class Dummy
{
/// <summary></summary>
[DataMember(Order = 1, Name = nameof(Value))]
public Any.Container Any { get => new Any.Container(Value); set => Value = value.Value; }
/// <summary>Object</summary>
public object? Value;
}
Сериализация
RuntimeTypeModel.Default.Add(typeof(Any.Container), false).SetSurrogate(typeof(Any));
var ms = new MemoryStream();
RuntimeTypeModel.Default.Serialize(ms, new Dummy { Value = "Hello world" });
Dummy dummy = RuntimeTypeModel.Default.Deserialize(typeof(Dummy), ms.ToArray().AsMemory()) as Dummy;
Во- первых, поскольку вы говорите «где у нас есть пара классов , которые нужно обернуть» (выделено мной), мне интересно, действительно ли вы хотите здесь, а не . protobuf-net поддерживает
syntax = "proto3";
message SomeType {
oneof Content {
Foo foo = 1;
Bar bar = 2;
Blap blap = 3;
}
}
message Foo {}
message Bar {}
message Blap {}
Это будет реализовано (с помощью инструментов схемы protobuf-net) как:
private global::ProtoBuf.DiscriminatedUnionObject __pbn__Content;
[global::ProtoBuf.ProtoMember(1, Name = @"foo")]
public Foo Foo
{
get => __pbn__Content.Is(1) ? ((Foo)__pbn__Content.Object) : default;
set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(1, value);
}
public bool ShouldSerializeFoo() => __pbn__Content.Is(1);
public void ResetFoo() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 1);
[global::ProtoBuf.ProtoMember(2, Name = @"bar")]
public Bar Bar
{
get => __pbn__Content.Is(2) ? ((Bar)__pbn__Content.Object) : default;
set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(2, value);
}
public bool ShouldSerializeBar() => __pbn__Content.Is(2);
public void ResetBar() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 2);
[global::ProtoBuf.ProtoMember(3, Name = @"blap")]
public Blap Blap
{
get => __pbn__Content.Is(3) ? ((Blap)__pbn__Content.Object) : default;
set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(3, value);
}
public bool ShouldSerializeBlap() => __pbn__Content.Is(3);
public void ResetBlap() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 3);
необязательно с перечислением, чтобы помочь:
public ContentOneofCase ContentCase => (ContentOneofCase)__pbn__Content.Discriminator;
public enum ContentOneofCase
{
None = 0,
Foo = 1,
Bar = 2,
Blap = 3,
}
Этот подход может быть проще и предпочтительнее , чем .
На :
Краткая версия: на сегодняшний день у protobuf-net не было конкретного запроса на реализацию . Вероятно, это не такой уж большой объем работы — просто: этого еще не было . Похоже, вы ссылаетесь здесь как на protobuf-net , так и на библиотеки Google, и используете реализацию Google . Это хорошо, но protobuf-net вообще не собирается его использовать — он не знает об API Google в этом контексте, поэтому:
Я был бы более чем счастлив посмотреть на это вместе с вами со стороны protobuf-net. В конечном счете, время/доступность всегда являются ограничивающим фактором, поэтому я отдаю предпочтение функциям, которые пользуются спросом. Я думаю, вы можете быть первым, кто спрашивает меня о