Вложенные объекты с Бондом
Я пытаюсь использовать Microsoft 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);
Но я получаю исключения (такие KeyNotFoundException) в Serialize.To(writer, src);
,
Я тоже пытался добавить [Bond.Schema]
в IFoo
, но тогда Deserialize<Bar>.From(reader);
не удается...
Как я могу сериализовать Bar
класс, который содержит некоторые Foo
класс с Бондом, не получая исключений, как это?
1 ответ
Если вы хотите использовать интерфейсы с поведенческими различиями (а не структурными различиями), хитрость заключается в том, чтобы предоставить десериализатору фабрику, чтобы он знал, как создать конкретный экземпляр 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");
}
}
}
}
Если вам нужны как поведенческие, так и структурные различия, то вам нужно соединить это с полиморфизмом и bonded<IFoo>
поле, так что вы можете отложить десериализацию, пока у вас не будет достаточно информации о типе, чтобы выбрать правильную реализацию. (Полиморфизм явный и согласен в Бонде.)
Я бы показал пример этого, но при написании этого ответа на 2018-02-21, я обнаружил ошибку в обработке классов с [Bond.Schema]
которые реализуют интерфейсы с [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!"