AqlaSolutions / AqlaSerializer

Binary serializer with full .NET support!
http://www.aqla.net
Other
17 stars 3 forks source link

AqlaSerializer.ProtoException: Trap count > 1, will be mismatched with next NoteObject #40

Closed inethui closed 2 years ago

inethui commented 2 years ago

I got the following exception when trying to deserialize an object. Basically I serialized an object into MemoryStream and then trying to deserialize it, but got this error. What does the error message mean? Is it something we didn't do well?

AqlaSerializer.ProtoException: Trap count > 1, will be mismatched with next NoteObject
   at AqlaSerializer.ProtoReader.TrapNextObject(Int32 newObjectKey)
   at AqlaSerializer.NetObjectHelpers.ReadNetObject_Start(Object& value, ProtoReader source, Type& type, NetObjectOptions options, Int32& typeKey, Boolean handleMissingKeys, Boolean replaceOldValue)
   at proto_478(Object , ProtoReader )
   at AqlaSerializer.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source, Boolean isRoot)
   at AqlaSerializer.ProtoReader.ReadTypedObject(Object value, Int32 key, ProtoReader reader, Type type)
   at proto_1512(Object , ProtoReader )
   at AqlaSerializer.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source, Boolean isRoot)
   at AqlaSerializer.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, Object value, Boolean noAutoCreate, Boolean isRoot)
   at AqlaSerializer.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, Int64 length, SerializationContext context)
   at AqlaSerializer.Serializer.Deserialize(Type type, Stream source)
inethui commented 2 years ago

I'm currently stuck on this issue. Any help is highly appreciated.

AqlaSolutions commented 2 years ago

It's possible that your scenario is not supported by the reference tracking system. I can't say what exactly causes this without a reproducable example. Try removing fields one by one until the issue disappears. Then send me an isolated repro.

AqlaSolutions commented 2 years ago

To be more clear, TrapNextObject is used to set object id which is used by a following NoteObject call when an instance is created. If the next serializer in the chain can't create object instance right away, it should call ReserveNoteObject to store the id in a stack for later use. Either way, next TrapNextObject call should not happen before any of these. This exception is thrown when calling TrapNextObject while previous trap hasn't been utilized yet. Allowing such calls would lead to loosing previously stored object id. So you need to find out which type of serializer called TrapNextObject previous time (without throwing) and why next serializer doesn't utilize the trap.

inethui commented 2 years ago

Finally, I was able to replicate this issue with a simple test case. The call "Serializer.DeepClone(inst);" will fail with error message like "AqlaSerializer.ProtoException : Trap count > 1, will be mismatched with next NoteObject".

Could you please take a look and see what's wrong? Thanks a lot for your help!

        private class Root
        {
            private UniqueIds2 _uniqueIds2;
            private Information _inputField;

            public UniqueIds2 InstrumentIds { get => _uniqueIds2; set => _uniqueIds2 = value; }
            public Information InputField { get => _inputField; set => _inputField = value; }
        }

        private sealed class Information
        {
            private object _data;

            public object Data { get => _data; set => _data = value; }
        }

        private class UniqueIds2
        {
            private long _uniqueId;
            private long _repeatableId;

            public UniqueIds2(long uniqueId, long repeatableId)
            {
                UniqueId = uniqueId;
                RepeatableId = repeatableId;
            }

            public long UniqueId { get => _uniqueId; private set => _uniqueId = value; }
            public long RepeatableId { get => _repeatableId; private set => _repeatableId = value; }
        }

        [Fact]
        public void Test()
        {
            var metaType1 = AqlaSerializer.Meta.RuntimeTypeModel.Default.Add(typeof(Root), false);
            metaType1.DefaultFormat = ValueFormat.Reference;
            metaType1.UseConstructor = false;
            metaType1.IgnoreListHandling = true;

            var metaType2 = AqlaSerializer.Meta.RuntimeTypeModel.Default.Add(typeof(Information), false);
            metaType2.DefaultFormat = ValueFormat.Reference;
            metaType2.UseConstructor = false;
            metaType2.IgnoreListHandling = true;

            AqlaSerializer.Meta.MetaType metaType3 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(Information)];
            var metaField1 = metaType3.AddField(1, "_data");
            metaField1.SetSettings(x => { x.V.Format = ValueFormat.Reference; });
            metaField1.SetSettings(x => x.V.WriteAsDynamicType = true, 0);

            AqlaSerializer.Meta.MetaType metaType4 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(Root)];
            var metaField2 = metaType4.AddField(1, "_inputField");
            metaField2.SetSettings(x => { x.V.Format = ValueFormat.Reference; });

            var metaType5 = AqlaSerializer.Meta.RuntimeTypeModel.Default.Add(typeof(UniqueIds2), false);
            metaType5.DefaultFormat = ValueFormat.Reference;
            metaType5.UseConstructor = false;
            metaType5.IgnoreListHandling = true;

            AqlaSerializer.Meta.MetaType metaType6 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(UniqueIds2)];
            var metaField3 = metaType6.AddField(1, "_repeatableId");
            metaField3.SetSettings(x => { x.V.Format = ValueFormat.Compact; });
            var metaField4 = metaType6.AddField(2, "_uniqueId");
            metaField4.SetSettings(x => { x.V.Format = ValueFormat.Compact; });

            AqlaSerializer.Meta.MetaType metaType8 = AqlaSerializer.Meta.RuntimeTypeModel.Default[typeof(Root)];
            var metaField5 = metaType8.AddField(2, "_uniqueIds2");
            metaField5.SetSettings(x => { x.V.Format = ValueFormat.Reference; });

            var inst = new Root();
            inst.InstrumentIds = new UniqueIds2(1001, 1001);

            var columnData = new Information();
            columnData.Data = "test";
            inst.InputField = columnData;

            Serializer.DeepClone(inst);
        }
AqlaSolutions commented 2 years ago

Fixed in develop branch

inethui commented 2 years ago

Great! Thanks for the quick fix. Just be curious, what was the problem? It has take me hours to replicate it with a simple test case.

AqlaSolutions commented 2 years ago

See https://github.com/AqlaSolutions/AqlaSerializer/commit/4f6a77097e96ee609ff5d2e8d73165d45a8afd02

inethui commented 2 years ago

I see, thanks.

inethui commented 2 years ago

I just verified that the issue is fixed with the develop branch. Can you publish it in nuget? We can only use nuget packages for our official builds.

AqlaSolutions commented 2 years ago

Unfortunately, I uninstalled VS 2019 and it's not possible to make the full build with all supported platforms using VS 2022. It may take some time before I will be able to publish the new release.

inethui commented 2 years ago

Any update on publishing the new release? Thanks

AqlaSolutions commented 2 years ago

Not today unless you know how to build Portable Class Library project with VS 2022.

inethui commented 2 years ago

Any update on this? Thanks

AqlaSolutions commented 2 years ago

Released

inethui commented 2 years ago

Cool, thank a lot!