使用protobuf序列化F#区分联合

stm*_*max 6 f# protobuf-net

有没有办法让protobuf序列化/反序列化F#的歧视联盟?

我正在尝试使用protobuf序列化消息.消息是F#记录和受歧视的联合.

序列化似乎适用于记录,但我不能让它与歧视的联合工作.

在下面的代码中,测试testMessageA和testMessageB是绿色的.测试testMessageDU为红色.

module ProtoBufSerialization

open FsUnit
open NUnit.Framework

open ProtoBuf

type MessageA = {
  X: string;
  Y: int;
}

type MessageB = {
  A: string;
  B: string;
}

type Message =
| MessageA of MessageA
| MessageB of MessageB

let serialize msg =
  use ms = new System.IO.MemoryStream()
  Serializer.SerializeWithLengthPrefix(ms, msg, PrefixStyle.Fixed32)
  ms.ToArray()

let deserialize<'TMessage> bytes =
  use ms = new System.IO.MemoryStream(buffer=bytes)
  Serializer.DeserializeWithLengthPrefix<'TMessage>(ms, PrefixStyle.Fixed32)

[<Test>]
let testMessageA() =
  let msg = {X="foo"; Y=32}
  msg |> serialize |> deserialize<MessageA> |> should equal msg

[<Test>]
let testMessageB() =
  let msg = {A="bar"; B="baz"}
  msg |> serialize |> deserialize<MessageB> |> should equal msg

[<Test>]
let testMessageDU() =
  let msg = MessageA {X="foo"; Y=32}
  msg |> serialize |> deserialize<Message> |> should equal msg
Run Code Online (Sandbox Code Playgroud)

我尝试在类型Message上添加ProtoInclude和KnownType等不同属性,在MessageA和MessageB类型上添加CLIMutable,但似乎没有任何帮助.

我不想将我的DU映射到类以使序列化工作......

Mar*_*ell 3

我已经使用了您非常有用的生成输出,看起来基本上一切正常 -除了子Message.MessageA类型。这些几乎可以工作 - 它们本质上与“自动元组”代码(匹配所有成员的构造函数)相同,只是自动元组当前不适用于子类型。

我认为应该可以通过扩展自动元组代码以在这种情况下工作来调整代码以自动工作(我正在尝试考虑任何可能的不良副作用,但我没有看到任何)。我没有具体的时间框架,因为我需要平衡多个项目和全职日常工作、家庭、志愿者工作等之间的时间。

从短期来看,以下 C# 足以使其工作,但我不认为这会是一个有吸引力的选择:

RuntimeTypeModel.Default[typeof(Message).GetNestedType("MessageA")]
                .Add("item").UseConstructor = false;
RuntimeTypeModel.Default[typeof(Message).GetNestedType("MessageB")]
                .Add("item").UseConstructor = false;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,这里的属性没有帮助,应该避免:

| [<ProtoMember(1)>] MessageA of MessageA
| [<ProtoMember(2)>] MessageB of MessageB
Run Code Online (Sandbox Code Playgroud)

如果他们做了什么,他们就会重复……的意图<ProtoInclude(n)>。如果在那里指定它们更方便,那可能会很有趣。但我发现真正有趣的是 F# 编译器完全忽略AttributeUsageAttribute,它[ProtoMember]是:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,
    AllowMultiple = false, Inherited = true)]
public class ProtoMemberAttribute {...}
Run Code Online (Sandbox Code Playgroud)

是的,F# 编译器显然(非法)将其固定在方法上:

[ProtoMember(1)]
[CompilationMapping(SourceConstructFlags.UnionCase, 0)]
public static ProtoBufTests.Message NewMessageA(ProtoBufTests.MessageA item)
Run Code Online (Sandbox Code Playgroud)

顽皮的 F# 编译器!