amoraller / protobuf-net

Automatically exported from code.google.com/p/protobuf-net
Other
0 stars 0 forks source link

Bug with serializing List<T> where T inherits from some base type (when using pregenerated serializer) #331

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
E-mail: artur.drobinskiy@gmail.com
==============
Here's the simplified model:
    [ProtoContract]
    public class Tree
    {
        [ProtoMember(1, AsReference = true)]
        public TreeNode RootNode { get; set; }
    }

    [ProtoContract]
    public class TreeNode : TreeNodeBase
    {
        [ProtoMember(1, AsReference = true)]
        public IList<TreeNode> ChildNodes { get; set; }
    }

    [ProtoContract, ProtoInclude(99, typeof(TreeNode))]
    public class TreeNodeBase
    {
        [ProtoMember(101)]
        public int Id { get; set; }
    }

The problem is when I use pregenerated serializer for it.
At deserialization point it throws:
 Test method SerializerConsumer.Consumer.TestSerializeDeserialize threw exception: 
System.InvalidCastException: Unable to cast object of type 'Model.TreeNode' to 
type 'Model.Tree'.

Attached is the solution to reproduce the problem.
There are 3 projects:
1) a Model project (with the model above)
2) SerializerGenerator (with one MsTest to pre-generate a serializer when the 
test is run, and another MsTest which shows that everything's ok if serializer 
from Full version is used)
3) SerializerConsumer with a test that throws an exception.

What is the expected output? What do you see instead?
I expect the Tree to deserialize correctly.
I see an exception instead.

What version of the product are you using? On what operating system?
Latest, win 8 x64 (though, it doesn't matter).

HERE ARE MY THOUGHTS :)
Taking a look at generated code in ILSpy (reflector) there are 2 problems.
1) in Write(Tree tree, ProtoWriter protoWriter)
there are following lines:
        TreeNode expr_2D = tree.RootNode;
    if (expr_2D != null)
    {
        ProtoWriter.WriteFieldHeader(1, WireType.String, protoWriter);
        SubItemToken token = ProtoWriter.StartSubItem(expr_2D, protoWriter);
        ProtobufCacheSerializer.Write(expr_2D, protoWriter);  //Here we should call ProtobufCacheSerializer.Write((TreeNodeBase)expr_2D, protoWriter); or better just define expr_20 as TreeNodeBase
        ProtoWriter.EndSubItem(token, protoWriter);
    }

2) in Write(TreeNode treeNode, ProtoWriter protoWriter):
        IList<TreeNode> expr_2D = treeNode.ChildNodes;
    if (expr_2D != null)
    {
        IList<TreeNode> list = expr_2D;
        foreach (TreeNode arg_51_0 in list)
        {
            ProtoWriter.WriteFieldHeader(1, WireType.String, protoWriter);
            BclHelpers.WriteNetObject((object)arg_51_0, protoWriter, 1, BclHelpers.NetObjectOptions.AsReference | BclHelpers.NetObjectOptions.UseConstructor); //Here we should pass "2" as a @key, because 2 is for TreeNodeBase (and 1 is for TreeNode)
        }
    }
Considering:
this.knownTypes = new Type[]
    {
        typeof(TreeNode),
        typeof(Tree),
        typeof(TreeNodeBase)
    };

By correcting this in the generated code it works well now.

So, is this really bugs or am i misusing protobuf?
Thanks!

Original issue reported on code.google.com by artur.dr...@gmail.com on 19 Oct 2012 at 6:23

Attachments:

GoogleCodeExporter commented 8 years ago
Interesting; I have a single-file repro of this in my local test rig, and oddly 
the *order* matters. Investigating. Thanks for the report and analysis.

Original comment by marc.gravell on 19 Oct 2012 at 7:39

GoogleCodeExporter commented 8 years ago
Sounds Exactly like my issue (299) where Compile() re-orders the list.

Original comment by bub...@yahoo.co.uk on 19 Oct 2012 at 7:53