zhejiushizhuce / protobuf-net

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

NullReferenceException during deserialization of array #154

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
NullReferenceException during deserialization of array.

========
URL: http://protobuf-net.googlecode.com/svn/branches/v1
Repository Root: http://protobuf-net.googlecode.com/svn
Repository UUID: b72047a4-3652-0410-9617-b3994939e97b
Revision: 358
==========

Tryied to run following code and get NullReference ecxeption in 
DeserializeWithLengthPrefix function.

Ran this code under mono2.6 (Ubuntu 10.10) and .NET 3.5

[code]
using System;
using ProtoBuf;
using System.IO;

namespace ProtobufTest
{
    [ProtoContract]
    public class Nested
    {
        [ProtoMember(1)]
        public int SomeInt{get;set;}    

        public Nested(){}
    }

    [ProtoContract]
    public class Test
    {
        private Nested[] nested=new Nested[10];

        [ProtoMember(1)]
        public Nested[] Nested{get {return nested;}}

        public Test()
        {
            for(int i=0;i<nested.Length;i++)
                nested[i]=new Nested();
        }
    }

    class MainClass
    {
        public static void Main(string[] args)
        {

            Test test1 = new Test();
            Test test2 = new Test();

            using (FileStream fs = new FileStream("test.proto", FileMode.Create))
            {
                ProtoBuf.Serializer.SerializeWithLengthPrefix<Test>(fs, test1, PrefixStyle.Fixed32);
            }

            using (FileStream fs = new FileStream("test.proto", FileMode.Open))
            {
                test2 = ProtoBuf.Serializer.DeserializeWithLengthPrefix<Test>(fs, PrefixStyle.Fixed32);
            }
        }

    }
}
[/code]

Original issue reported on code.google.com by geolay...@gmail.com on 8 Feb 2011 at 1:41

GoogleCodeExporter commented 9 years ago
An array is mapped to the "repeated" type in .proto terms, which means that it 
is treated as *appending* items; and in array terms the only way to append is 
to replace, copy and re-assign the array. It is failing because there is no 
"set", but even without this *in v1* it would still run the .ctor, so you would 
end up with an array length 4.

A private set would be sufficient, of course.
Note that if you use a List<T> it will happily work with or without a set, as 
it knows it can just .Add() to the existing list.

In the above scenario (array fixed size and can't be reassigned externally) I 
would be tempted to do:

    [ProtoMember(1)]
    private Nested Nested0 { get {return nested[0];} set {nested[0] = value;}}
    [ProtoMember(2)]
    private Nested Nested1 { get {return nested[1];} set {nested[1] = value;}}

obviously is inappropriate if the array may be sized dynamically, but in that 
case I would expose a List<T>. In v2 you can skip the .ctor, but in v1 (given 
the above) I would be very tempted to use a before-deserialization callback 
([OnDeserializing]) to wipe the array/list

Original comment by marc.gravell on 8 Feb 2011 at 2:06

GoogleCodeExporter commented 9 years ago
I was faced with a similar problem, with a fixed-size array. But as the size 
was 30, I couldn't imagine myself writing 30 different properties for each 
index in the array.
I used instead a proxy Dictionary property for serialization/deserialization.

[ProtoMember(1, IsRequired = true)]
private SortedDictionary<int, char[]> _serializableCharArray
{
    get
    {
        SortedDictionary<int, char[]> dic = new SortedDictionary<int, char[]>();
        for (int i = 0; i < _charArrays.Length; ++i)
        {
            char[] charArray = _charArrays[i];
            if (charTable != null)
                dic[i] = charArray;
        }
        return dic;
    }

    set
    {
        foreach (KeyValuePair<int, CharTable> indexCharTable in value)
        {
            _charArrays[indexCharTable.Key] = indexCharTable.Value;
        }
    }
}

Original comment by l...@virtuoz.com on 26 May 2011 at 12:19