Exception serializing custom collection

孤者浪人 提交于 2019-12-24 17:55:56

问题


Thanks for the great library Marc and for all the answers on SO.

I'm using protobuf-net r480.zip on .NET 4.0.zip from http://code.google.com/p/protobuf-net/downloads/list. Is this the latest stable release?

I'm having trouble serializing a custom collection.

public static void TestSerialization() {
    using (var stream = new MemoryStream()) {
        var b1 = new B1 { 1 };
        // Throws System.ArgumentException: Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be subclassed
        Serializer.Serialize(stream, b1);
    }

    using (var stream = new MemoryStream()) {
        var b2 = new B2 { 2 };
        Serializer.Serialize(stream, b2);
        stream.Position = 0;
        var b2Deserialized = Serializer.Deserialize<B2>(stream);

        // This fails because b2Deserialized.Count is 0.
        Assert.AreEqual(1, b2Deserialized.Count);
    }

    using (var stream = new MemoryStream()) {
        RuntimeTypeModel.Default[typeof(A2<int>)].AddSubType(1000, typeof(B2));
        var b2 = new B2 { 2 };
        // Throws System.ArgumentException: Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be subclassed
        Serializer.Serialize(stream, b2);
    }
}

[ProtoContract]
[ProtoInclude(1000, typeof(B1))]
public class A1<T> : List<T> { }

[ProtoContract]
public class B1 : A1<int> { }

[ProtoContract]
public class A2<T> : List<T> { }

[ProtoContract]
public class B2 : A2<int> { }

Thanks for the anser Marc, removing the Proto attributes works great if just using a list. Unfortunately, the actual code is more complex - the derived collection contains other values (in the real code the template parameter in of type Entity instead of int and the member in the collection is a reference to the parent of the elements in the collection). Here is a better representation.

    public static void TestSerialization() {
        using (var stream = new MemoryStream()) {
            var b = new B { 23 };
            b.SomeValue = "abc";
            Serializer.Serialize(stream, b);

            stream.Position = 0;
            var deserialized = Serializer.Deserialize<B>(stream);
            Assert.AreEqual(1, deserialized.Count);
            Assert.AreEqual(b[0], deserialized[0]);

            // This fails because deserialized.SomeValue == null
            Assert.AreEqual(b.SomeValue, deserialized.SomeValue);
        }
    }

    public class A<T> : List<T> { }

    public class B : A<int>
    {
        [ProtoMember(1)]
        public string SomeValue;
    }

回答1:


IMO the error message is quite clear: inheritance is not supported on lists. This is because lists don't have a place-holder, so there is nowhere to store this information. In XML terms (noting that protobuf is nothing like XML), it is like the output of (if you are familiar with XmlSerializer):

[XmlElement("item")]
public List<Foo> Items { get; set; }

Which outputs just:

<item>...</item>
<item>...</item>
<item>...</item>

(but no "Items" node - there exists nowhere that you could say anything about the list itself)

I'm trying to understand what you are trying to represent with that inheritance. Frankly, I'm not seeing anything useful - since lists have inbuilt behaviour, you don't need to define contracts for these at all; just use them as lists.

In the general sense, it is usually better to encapsulate lists, not inherit them.



来源:https://stackoverflow.com/questions/11614968/exception-serializing-custom-collection

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!