You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ArgumentException when serializing a nested struct #189
Using 0.8.0 (and 0.9.0-beta1) it looks like there's a problem with serializing nested structures, the following code (which is a simplified example of what we've got) produces this exception:
Exception: ArgumentException
Message: Field '_field' defined on type 'Example.NestedStruct' is not a field on the target object which is of type 'MsgPack.StreamPacker'.
StackTrace:
at System.Reflection.RtFieldInfo.CheckConsistency(Object target)
at System.Reflection.RtFieldInfo.InternalGetValue(Object obj, StackCrawlMark& stackMark)
at System.Reflection.RtFieldInfo.GetValue(Object obj)
at MsgPack.Serialization.EmittingSerializers.Generated.Example_NestedStructSerializer2.PackValueOf_field(Packer , NestedStruct )
at MsgPack.Serialization.PackHelpers.PackToArray[TObject](PackToArrayParameters1& parameter) at MsgPack.Serialization.EmittingSerializers.Generated.Example_NestedStructSerializer2.PackToCore(Packer , NestedStruct ) at MsgPack.Serialization.MessagePackSerializer1.PackTo(Packer packer, T objectTree)
at MsgPack.Serialization.EmittingSerializers.Generated.Example_OuterClassSerializer1.PackValueOfStruct(Packer , OuterClass )
at MsgPack.Serialization.PackHelpers.PackToArray[TObject](PackToArrayParameters1& parameter) at MsgPack.Serialization.EmittingSerializers.Generated.Example_OuterClassSerializer1.PackToCore(Packer , OuterClass ) at MsgPack.Serialization.MessagePackSerializer1.PackTo(Packer packer, T objectTree)
at MsgPack.Serialization.MessagePackSerializer`1.PackSingleObject(T objectTree)
at Example.Program.Main(String[] args)
using System;
using System.Runtime.Serialization;
using MsgPack.Serialization;
namespace Example
{
[DataContract]
public struct NestedStruct
{
[DataMember]
private int _field;
}
[DataContract]
public class OuterClass
{
[DataMember]
public NestedStruct Struct { get; set; }
}
class Program
{
static void Main(string[] args)
{
var context = new SerializationContext();
context.GetSerializer<OuterClass>().PackSingleObject(new OuterClass());
}
}
}
It looks like there's a problem with the generated code, but I'm not sure how to debug that part, sorry. If you change NestedStruct to a class then it works, however.
I've tracked it down to the EmitGetField method of the SerializerBuilder<TContext, TConstruct> class - when invoking the FieldInfo.GetValue method you need to box the value type before passing it in as an argument. That's a simple change and gets it to serialize properly.
The problem is when it comes to deserializing. Calling FieldInfo.SetValue on a boxed value will change the value of the copied value that was boxed and not the original, i.e.
MyStruct myStruct = new MyStruct();
FieldInfo fieldInfo = typeof(MyStruct).GetField("Field");
fieldInfo.SetValue(myStruct, 123);
Console.WriteLine(myStruct.Field); // Writes 0, as SetValue received a boxed copy
I'm unsure how I can create the local, however, using the methods in the SerializerBuilder<TContext, TConstruct> class, as if there are more than one field it will try to create two locals with the same name. I also don't want to always create the local as it's rare to be using a struct in the first place. Any idea on what I can do in EmitSetField to declare a local on demand if it's a value type?
Hi, thank you for reporting.
Your idea sounds good. You can use DeclareLocal method of SerializerBuilder to avoid local name conflict (depends on TContext implementation).
If you are writing patch for this, I'll wait for your work.
Activity
samcragg commentedon Oct 23, 2016
I've tracked it down to the
EmitGetFieldmethod of theSerializerBuilder<TContext, TConstruct>class - when invoking theFieldInfo.GetValuemethod you need to box the value type before passing it in as an argument. That's a simple change and gets it to serialize properly.The problem is when it comes to deserializing. Calling
FieldInfo.SetValueon a boxed value will change the value of the copied value that was boxed and not the original, i.e.To get around this I need to use a local:
I'm unsure how I can create the local, however, using the methods in the
SerializerBuilder<TContext, TConstruct>class, as if there are more than one field it will try to create two locals with the same name. I also don't want to always create the local as it's rare to be using a struct in the first place. Any idea on what I can do inEmitSetFieldto declare a local on demand if it's a value type?yfakariya commentedon Oct 25, 2016
Hi, thank you for reporting.
Your idea sounds good. You can use
DeclareLocalmethod ofSerializerBuilderto avoid local name conflict (depends onTContextimplementation).If you are writing patch for this, I'll wait for your work.
Merge pull request #190 from samcragg/master