Closed aienabled closed 8 years ago
I will try latest source version now.
It seems IgnoreListHandling is working with it - I've got exception Repeated data (an array, list, etc) has inbuilt behavior and can't have fields
if it's set to true and no exception if false.
Yes, latest version is throw exception if I try to define fields for my custom collection type when metaType.IgnoreListHandling
is false. That's good.
However with metaType.IgnoreListHandling = true
the callback delegate metaType.Callbacks.AfterDeserialize
is still not called during deserialization.
I've investigated it a little and found that for my custom type the callback call is emitted by TypeSerializer.EmitCallbackIfNeeded()
method.
UPD. Yes, the callback IL code is emitted. However, I still not understand why it doesn't called.
I made a test for your case and it doesn't seems to be reproducable on my side:
using System.Collections.Generic;
using AqlaSerializer.Meta;
using NUnit.Framework;
using ProtoBuf;
namespace AqlaSerializer.unittest.Aqla
{
[TestFixture]
public class Issue7ListHandlingCallbacksProto
{
[ProtoContract]
public class Container
{
[ProtoMember(1)]
public TestList Member { get; set; }
}
[ProtoContract(IgnoreListHandling = true)]
public class TestList : List<int>
{
[ProtoIgnore]
public int SomeValue { get; set; }
[ProtoMember(1)]
public List<int> InnerList { get; set; }
[ProtoAfterDeserialization]
public void Callback()
{
SomeValue = 12345;
}
}
[Test]
public void Execute()
{
var tm = TypeModel.Create();
tm.AutoCompile = true;
tm.SkipCompiledVsNotCheck = true;
var list = new List<int>() { 1, 2, 3 };
var v = tm.DeepClone(new Container() { Member = new TestList() { InnerList = list } });
Assert.That(v.Member.SomeValue, Is.EqualTo(12345));
Assert.That(v.Member.InnerList, Is.EqualTo(list));
}
}
}
If you are using attributes from aqlaserializer.dll, please try the recent version as it has fixes to the [SerializableType]
attribute:
https://github.com/AqlaSolutions/AqlaSerializer/releases
Thanks. I'm not using attributes. I've extended your test to make it closer to what we have. It seems the collection itself (without Container) could be properly (de)serialized and callback is called, but everything is broken when the collection is nested inside a Container object.
Please try this:
[TestFixture]
public class Issue7ListHandlingCallbacksProto
{
[Test]
public void Execute()
{
var model = TypeModel.Create();
model.AutoAddMissingTypes = false;
model.AutoCompile = false;
// register container type
this.RegisterListType(model, typeof(TestList<string>));
model.Add(typeof(Container), true).AddField(1, "Content").AsReference = false;
model.SkipCompiledVsNotCheck = true;
model.CompileInPlace();
var originalList = new List<string>() { "1", "2", "3" };
// check without container
{
var deserializedList = model.DeepClone(new TestList<string>(originalList));
Assert.That(deserializedList.InnerList, Is.EqualTo(originalList));
Assert.That(deserializedList.IsDeserializationCalled, Is.EqualTo(true));
// please note we've set metaType.UseConstructor = false
Assert.That(deserializedList.IsDefaultConstructorCalled, Is.EqualTo(false));
}
// check with container
{
var deserialized = model.DeepClone(new Container() { Content = new TestList<string>(originalList) });
var deserializedList = deserialized.Content;
Assert.That(deserializedList.InnerList, Is.EqualTo(originalList));
Assert.That(deserializedList.IsDeserializationCalled, Is.EqualTo(true));
// please note we've set metaType.UseConstructor = false
Assert.That(deserializedList.IsDefaultConstructorCalled, Is.EqualTo(false));
}
}
private void RegisterListType(RuntimeTypeModel model, Type type)
{
var metaType = model.Add(type, applyDefaultBehaviourIfNew: false);
metaType.UseConstructor = false;
metaType.IgnoreListHandling = true;
metaType.AsReferenceDefault = true;
metaType.AddField(1, "innerList").AsReference = false;
metaType.Callbacks.AfterDeserialize = type.GetMethod(
"AfterDeserializeAsSubObject",
BindingFlags.NonPublic | BindingFlags.Instance);
Assert.That(metaType.Callbacks.AfterDeserialize, !Is.EqualTo(null));
}
public class Container
{
public TestList<string> Content { get; set; }
}
public class TestList<T> : TestBaseClass, IList<T>
{
private readonly List<T> innerList;
public TestList(List<T> list)
{
this.innerList = list;
}
public TestList() : base()
{
}
public int Count { get; }
public List<T> InnerList => this.innerList;
public bool IsReadOnly { get; }
public T this[int index]
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public void Add(T item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
throw new NotImplementedException();
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}
public int IndexOf(T item)
{
throw new NotImplementedException();
}
public void Insert(int index, T item)
{
throw new NotImplementedException();
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
public abstract class TestBaseClass
{
public bool IsDefaultConstructorCalled;
public bool IsDeserializationCalled;
protected TestBaseClass()
{
this.IsDefaultConstructorCalled = true;
}
[AfterDeserializationCallback]
protected void AfterDeserializeAsSubObject(SerializationContext context)
{
this.IsDeserializationCalled = true;
}
}
Resolved...
The fix is confirmed. Thanks!
Hello! I've a custom collection class which is implements IList and it's basically just a wrapper over a list instance (and the only serialized field is the inner list).
The problem is that a custom callback delegate ( from class definition it works perfectly well.
metaType.Callbacks.AfterDeserialize = ...
) is never called in this case. But when I remove IListI know there is a setting for ignoring default list handling -
metaType.IgnoreListHandling = true
- but it doesn't resolve the issue. It seems the type continue to be handled like a collection by AqlaSerializer. I've tried with this setting and without it and still get the same proto-scheme:Regards!