我试图使用Microsoft Bond来序列化嵌套对象。但是Bond会抛出内部错误(例如KeyNotFoundException)。
我的课程:
interface IFoo
{
}
[Bond.Schema]
class Foo1 : IFoo
{
[Bond.Id(0)]
public string Foo1Field { get; set; }
}
[Bond.Schema]
class Bar
{
[Bond.Id(0)]
public IFoo SomeFooInstance { get; set; }
}
然后我创建一个实例并序列化:
var src = new Bar() { SomeFooInstance = new Foo1() { Foo1Field = "Str" }};
var output = new OutputBuffer();
var writer = new CompactBinaryWriter<OutputBuffer>(output);
Serialize.To(writer, src);
var input = new InputBuffer(output.Data);
var reader = new CompactBinaryReader<InputBuffer>(input);
var dst = Deserialize<Bar>.From(reader);
但是我在Serialize.To(writer, src);
获得了异常(例如KeyNotFoundException)。
我还尝试将[Bond.Schema]
添加到IFoo
,但Deserialize<Bar>.From(reader);
失败了......
如何使用Bond序列化包含某些Bar
类的Foo
类,而不会出现类似的异常?
答案 0 :(得分:0)
如果你想使用具有行为差异(而不是结构差异)的接口,诀窍是为反序列化器提供工厂,以便它知道如何在需要时创建IFoo
的具体实例。请注意,实现不具有[Bond.Schema]
属性,因为它们都实现了IFoo
模式。
namespace NS
{
using System;
using Bond.IO.Unsafe;
using Bond.Protocols;
internal static class Program
{
[Bond.Schema]
interface IFoo
{
[Bond.Id(10)]
string FooField { get; set; }
}
[Bond.Schema]
class Bar
{
[Bond.Id(20)]
public IFoo SomeFooInstance { get; set; }
}
class AlwaysUppercaseFoo : IFoo
{
private string fooField;
public string FooField
{
get
{
return fooField;
}
set
{
fooField = value.ToUpperInvariant();
}
}
}
class IdentityFoo : IFoo
{
public string FooField { get; set; }
}
public static Expression NewAlwaysUppercaseFoo(Type type, Type schemaType, params Expression[] arguments)
{
if (schemaType == typeof(IFoo))
{
return Expression.New(typeof(AlwaysUppercaseFoo));
}
// tell Bond we don't handle the requested type, so it should use it's default behavior
return null;
}
public static Expression NewIdentityFoo(Type type, Type schemaType, params Expression[] arguments)
{
if (schemaType == typeof(IFoo))
{
return Expression.New(typeof(IdentityFoo));
}
// tell Bond we don't handle the requested type, so it should use it's default behavior
return null;
}
public static void Main(string[] args)
{
var src = new Bar() { SomeFooInstance = new IdentityFoo() { FooField = "Str" } };
var output = new OutputBuffer();
var writer = new CompactBinaryWriter<OutputBuffer>(output);
Bond.Serialize.To(writer, src);
{
var input = new InputBuffer(output.Data);
var deserializer = new Bond.Deserializer<CompactBinaryReader<InputBuffer>>(typeof(Bar), NewAlwaysUppercaseFoo);
var reader = new CompactBinaryReader<InputBuffer>(input);
var dst = deserializer.Deserialize<Bar>(reader);
Debug.Assert(dst.SomeFooInstance.FooField == "STR");
}
{
var input = new InputBuffer(output.Data);
var deserializer = new Bond.Deserializer<CompactBinaryReader<InputBuffer>>(typeof(Bar), NewIdentityFoo);
var reader = new CompactBinaryReader<InputBuffer>(input);
var dst = deserializer.Deserialize<Bar>(reader);
Debug.Assert(dst.SomeFooInstance.FooField == "Str");
}
}
}
}
如果您需要行为和结构上的差异,那么您需要将其与polymorphism和bonded<IFoo>
字段配对,以便您可以延迟反序列化,直到您有足够的类型信息来选择正确的实现。 (多态性是明确的,并且选择加入邦德。)
我会举一个这样的例子,但在2018-02-21写这个答案时,我找到了bug in the handling of classes with [Bond.Schema]
that implement interfaces with [Bond.Schema]
:界面中的字段被省略了。
目前,解决方法是将继承与类一起使用并使用虚拟属性。例如:
namespace NS
{
using System;
using Bond.IO.Unsafe;
using Bond.Protocols;
internal static class Program
{
enum FooKind
{
Unknown = 0,
AlwaysUppercase = 1,
Identity = 2,
}
// intentionally a class to work around https://github.com/Microsoft/bond/issues/801 but simulate an interface somewhat
[Bond.Schema]
class IFoo
{
[Bond.Id(0)]
public virtual string FooField { get; set; }
}
[Bond.Schema]
class Bar
{
[Bond.Id(0)]
public Bond.IBonded<IFoo> SomeFooInstance { get; set; }
[Bond.Id(1)]
public FooKind Kind { get; set; }
}
[Bond.Schema]
class AlwaysUppercaseFoo : IFoo
{
private string fooField;
public override string FooField
{
get
{
return fooField;
}
set
{
fooField = value.ToUpperInvariant();
}
}
[Bond.Id(0)]
public string JustAlwaysUppercaseFooField { get; set; }
}
[Bond.Schema]
class IdentityFoo : IFoo
{
[Bond.Id(42)]
public string JustIdentityFooField { get; set; }
}
static void RoundTripAndPrint(Bar src)
{
var output = new OutputBuffer();
var writer = new CompactBinaryWriter<OutputBuffer>(output);
Bond.Serialize.To(writer, src);
var input = new InputBuffer(output.Data);
var reader = new CompactBinaryReader<InputBuffer>(input);
var dst = Bond.Deserialize<Bar>.From(reader);
switch (dst.Kind)
{
case FooKind.Identity:
{
var fooId = dst.SomeFooInstance.Deserialize<IdentityFoo>();
Console.WriteLine($"IdFoo: \"{fooId.FooField}\", \"{fooId.JustIdentityFooField}\"");
}
break;
case FooKind.AlwaysUppercase:
{
var fooUc = dst.SomeFooInstance.Deserialize<AlwaysUppercaseFoo>();
Console.WriteLine($"UcFoo: \"{fooUc.FooField}\", \"{fooUc.JustAlwaysUppercaseFooField}\"");
}
break;
default:
Console.WriteLine($"Unknown Kind: {dst.Kind}");
break;
}
}
public static void Main(string[] args)
{
var o = new OutputBuffer();
var w = new CompactBinaryWriter<OutputBuffer>(o);
Bond.Serialize.To(w, new IdentityFoo() { FooField = "Str", JustIdentityFooField = "id" });
var src_id = new Bar()
{
SomeFooInstance = new Bond.Bonded<IdentityFoo>(new IdentityFoo() { FooField = "Str", JustIdentityFooField = "id" }),
Kind = FooKind.Identity
};
var src_uc = new Bar()
{
SomeFooInstance = new Bond.Bonded<AlwaysUppercaseFoo>(new AlwaysUppercaseFoo() { FooField = "Str", JustAlwaysUppercaseFooField = "I LIKE TO YELL!" }),
Kind = FooKind.AlwaysUppercase
};
RoundTripAndPrint(src_id);
RoundTripAndPrint(src_uc);
}
}
}
打印:
IdFoo: "Str", "id" UcFoo: "STR", "I LIKE TO YELL!"