使用 protobuf 序列化 F# 区分联合 [英] Serializing F# discriminated unions with protobuf

查看:23
本文介绍了使用 protobuf 序列化 F# 区分联合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有某种方法可以让 protobuf 序列化/反序列化 F# 的可区分联合?

Is there some way to get protobuf to serialize/deserialize F#'s discriminated unions?

我正在尝试使用 protobuf 序列化消息.消息是 F# 记录和可区分联合.

I'm trying to serialize messages with protobuf. Messages are F# records and discriminated unions.

序列化似乎适用于记录,但我无法让它与受歧视的联合一起工作.

Serialization seems to work fine for records but I cannot get it to work with discriminated unions.

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

In the following piece of code the tests testMessageA and testMessageB are green. The test testMessageDU is red.

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

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

I tried adding different attributes like ProtoInclude and KnownType on type Message, CLIMutable on types MessageA and MessageB,... but nothing seems to help.

我宁愿不必将我的 DU 映射到类来使序列化工作...

I'd prefer not having to map my DUs to classes to get serialization to work...

推荐答案

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

I've played with your very helpful generated output, and it looks like basically everything works - except the Message.MessageA sub-types. These very nearly work - they are essentially the same as the "auto-tuple" code (a constructor that matches all members), except that auto-tuples doesn't currently apply to sub-types.

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

I think it should be possible to tweak the code to work automatically, by extending the auto-tuple code to work in this scenario (I'm trying to think of any possible bad side-effects of that, but I'm not seeing any). I don't have a specific time-frame, as I need to balance time between multiple projects and a full-time day-job, and a family, and volunteer work, and (etc etc).

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

In the short term, the following C# is sufficient to make it work, but I don't expect this will be an attractive option:

RuntimeTypeModel.Default[typeof(Message).GetNestedType("MessageA")]
                .Add("item").UseConstructor = false;
RuntimeTypeModel.Default[typeof(Message).GetNestedType("MessageB")]
                .Add("item").UseConstructor = false;

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

As an aside, the attributes here are unhelpful and should be avoided:

| [<ProtoMember(1)>] MessageA of MessageA
| [<ProtoMember(2)>] MessageB of MessageB

如果他们做了任何事情,他们将复制的意图.不过,如果在那里指定它们更方便,那可能会很有趣.但我发现真正有趣的是 F# 编译器完全忽略了 AttributeUsageAttribute,对于 [ProtoMember] 来说,它是:

If they did anything, they would be duplicating the intent of <ProtoInclude(n)>. If it is more convenient to specify them there, that might be interesting, though. But what I find really interesting about that is that the F# compiler completely ignores the AttributeUsageAttribute, which for [ProtoMember] is:

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

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

Yes the F# compiler clearly stuck that (illegally) on a method:

[ProtoMember(1)]
[CompilationMapping(SourceConstructFlags.UnionCase, 0)]
public static ProtoBufTests.Message NewMessageA(ProtoBufTests.MessageA item)

顽皮的 F# 编译器!

naughty F# compiler!

这篇关于使用 protobuf 序列化 F# 区分联合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆