使用 Bond 嵌套对象

Nested objects with Bond

我正在尝试使用 Microsoft Bond 序列化嵌套对象。但是邦德抛出内部错误(例如 KeyNotFoundException)。

我的class是:

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 序列化包含一些 Foo class 的 Bar class 而不会出现这样的异常?

如果你想使用具有行为差异(而不是结构差异)的接口,诀窍是为反序列化器提供工厂,以便它知道如何在需要时创建 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> 字段配对,这样您就可以延迟反序列化,直到您有足够的类型信息来 select正确的实现。 (多态性是显式的,opt-in 在 Bond 中。)

我会展示一个例子,但是在 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!"