MessagePack.FSharpExtensions
MessagePack.FSharpExtensions copied to clipboard
Serializing discriminated union failed while using non-generic classes
Problem
I get an error when serializing A
in SimpleUnion
[<MessagePackObject>]
type SimpleUnion =
| A
| B of int
| C of int64 * float32
let resolver = WithFSharpDefaultResolver() :> IFormatterResolver
MessagePackSerializer.NonGeneric.Serialize(A.GetType(), A, resolver) // failed
The exception is
Message: System.TypeLoadException : Type
'MessagePack.FSharp.Formatters.MessagePack_Tests_DUTest\+SimpleUnion\+_AFormatter4'
from assembly 'MessagePack.FSharp.DynamicUnionResolver, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
is attempting to implement an inaccessible interface.
But it can serialize A
correctly with SimpleUnion2
which inserts a unit
field.
[<MessagePackObject>]
type SimpleUnion2 =
| A2
| AU2 of unit
| B2 of int
| C2 of int64 * float32
Here is my test case source
module MessagePack.Tests.DUTest
open Xunit
open MessagePack
let convert_nongeneric (value: 'a) =
let resolver = WithFSharpDefaultResolver() :> IFormatterResolver
let t = value.GetType()
let bin : byte[] = MessagePackSerializer.NonGeneric.Serialize(t, value :> obj, resolver)
let actual :'a = MessagePackSerializer.NonGeneric.Deserialize(t, bin, resolver) :?> 'a
Assert.Equal<'a>(value, actual)
[<MessagePackObject>]
type SimpleUnion =
| A
| B of int
| C of int64 * float32
[<MessagePackObject>]
type SimpleUnion2 =
| A2
| AU2 of unit
| B2 of int
| C2 of int64 * float32
[<Fact>]
let sampleA () =
convert_nongeneric A // failed
[<Fact>]
let ``sampleA2`` () =
convert_nongeneric A2 // success
[<Fact>]
let sampleAU () =
convert_nongeneric <| AU2 ()
[<Fact>]
let sampleB () =
convert_nongeneric (B 1)
convert_nongeneric (B2 1)
[<Fact>]
let sampleC () =
convert_nongeneric <| C(1L, 1.0f)
convert_nongeneric <| C2(1L, 1.0f)
Motivation
I am trying to porting this extension to Akka.Serialization.MessagePack, which uses MessagePackSerializer.NonGeneric to serialize a object.
This serialize function needs a given type value, which is get from obj.GetType()
in Akka implementation.
I use the serializaion here
Sorry for the delay in replying...
FSharp.Reflection.FSharpType.IsUnion(SimpleUnion.A.GetType())
returns true
, so DiscremenatedUnionResolver
will attempt to generate a formatter.
However, SimpleUnion.A.GetType()
returns type info of the concrete class generated by the compiler, MessagePackSerializer requires IMessagePackFormatter<SimpleUnion.A>
.
So far I have not found a way to solve this problem within DiscremenatedUnionResolver
.
Please try to check and use Type.BaseType
:
let t =
let t = A.GetType()
match t.BaseType with
| null -> t
| p when Reflection.FSharpType.IsUnion(p) -> p
| _ -> t
MessagePackSerializer.NonGeneric.Serialize(t, A, resolver)