我正在创建一个gRPC服务,我们决定选择使用protobuf的代码优先方法。现在,我遇到了一个场景,我们有几个需要包装的类。我们不希望在KnownTypes类中定义MyMessage (只是说明问题的示例名称)。因此,我试图使用任何类型,目前给我一些困难的包装。
示例代码具有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
。因为我不知道怎么做,也找不到很多信息,所以我想知道我是否走错了路。
如何在protobuf-net?
发布于 2021-12-07 13:41:26
首先,既然您说“我们有一个需要包装的类--两个类--”(强调我的),我想知道您在这里真正想要的是oneof
而不是Any
。protobuf支持oneof
的概念,尽管从代码优先的角度来看它并不明显。但想象一下我们(在合同第一意义上):
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,
}
这种方法可能比Any
更容易,也更可取。
论Any
短版本:到目前为止,protobuf还没有实现Any
的任何特定请求。这可能不是一项巨大的工作-简单地说,它还没有发生。看起来,这里引用的是protobuf和Google,并使用了Any
的Google实现。这很好,但是protobuf根本不打算使用它--它不知道在这种情况下Google,所以:实现IMessage
实际上不会对您有所帮助。
我非常乐意和你一起看Any
,从protobuf的角度看。最终,时间/可用性始终是限制因素,所以我优先考虑那些能够看到需求的特性。我认为您可能是第一个问我关于protobuf中Any
的人。
发布于 2022-05-27 16:09:12
我的任何实现。
[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 Container
{
/// <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 Container { Value = "Hello world" });
Container dummy = RuntimeTypeModel.Default.Deserialize(typeof(Container), ms.ToArray().AsMemory()) as Container;
https://stackoverflow.com/questions/70260050
复制相似问题