Dictionary<string, int> 使用 protobuf-net 序列化时抛出异常
Dictionary<string, int> throwing exception when serializing with protobuf-net
我正在尝试让 protobuf-net 与 Unity 一起工作。从我最近几天所做的大量研究来看,让两者一起正常工作似乎很复杂。在使用 IL2CPP 进行构建时,我现在遇到了一个无法解决的问题。
对于某些类型,序列化似乎有时会失败,例如 Dictionary 或 Dictionary 但对于 Dictionary 可以正常工作。为了清楚起见,我使用的是 nuget 的 protobuf-net 版本 2.4.6,因为它似乎完全不支持 v3(未实现的 IL2CPP 方法)。发生的错误是:
ExecutionEngineException: Attempting to call method 'ProtoBuf.Serializers.MapDecorator`3[[System.Collections.Generic.Dictionary`2[[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]::.cctor' for which no ahead of time (AOT) code was generated.
at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters) [0x00000] in <00000000000000000000000000000000>:0
at System.Reflection.MonoCMethod.DoInvoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0
at System.Reflection.MonoCMethod.Invoke (System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0
at System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.ValueMember.BuildSerializer () [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.ValueMember.get_Serializer () [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.MetaType.BuildSerializer () [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.MetaType.get_Serializer () [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.RuntimeTypeModel.Serialize (System.Int32 key, System.Object value, ProtoBuf.ProtoWriter dest) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.TypeModel.SerializeCore (ProtoBuf.ProtoWriter writer, System.Object value) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.TypeModel.Serialize (System.IO.Stream dest, System.Object value, ProtoBuf.SerializationContext context) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.TypeModel.Serialize (System.IO.Stream dest, System.Object value) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Serializer.Serialize[T] (System.IO.Stream destination, T instance) [0x00000] in <00000000000000000000000000000000>:0
at SerializationTests.SerializeObject[T] (T objectToSave) [0x00000] in <00000000000000000000000000000000>:0
at SerializationTests.AttemptSerializingData[T] (T dataToSerialize, Entry entry) [0x00000] in <00000000000000000000000000000000>:0
at Entry.OnGUI () [0x00000] in <00000000000000000000000000000000>:0
class 我正在尝试在副项目上进行序列化以进行测试,如下所示:
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
public class DataDictionnary<T, U>
{
[ProtoMember(1)] private readonly Dictionary<T, U> someDictionnary = new Dictionary<T, U>();
public DataDictionnary()
{
}
public void AddValue(T key, U value)
{
if (someDictionnary.ContainsKey(key)) return;
someDictionnary.Add(key, value);
}
public override string ToString()
{
string s = $"{GetType()}";
foreach (var key in someDictionnary.Keys)
{
s += $" ({key}, {someDictionnary[key]})";
}
return s;
}
}
有没有理由我只能用基元序列化某些类型的字典,我可以用来让它工作的任何东西?
编辑:我的 link.xml 文件应该可以防止代码剥离
<linker>
<assembly fullname="Test.Serialization" preserve="all"/>
<assembly fullname="protobuf-net" preserve="all"/>
</linker>
SerializationTests.cs
using System.IO;
using ProtoBuf;
public static class SerializationTests
{
public static void AttemptSerializingData<T>(T dataToSerialize, bool displayProto, Entry entry)
{
if (displayProto)
entry.AddLog(Serializer.GetProto<T>());
entry.AddLog($"Serializing {dataToSerialize}");
var serializedObject = SerializeObject(dataToSerialize);
entry.AddLog($"Done ({serializedObject.Length})");
var deserializedObject = DeserializeObject<T>(serializedObject);
entry.AddLog($"Done! {deserializedObject}");
}
private static T DeserializeObject<T>(byte[] bytes)
{
T result;
using (var stream = new MemoryStream(bytes))
{
result = Serializer.Deserialize<T>(stream);
}
return result;
}
private static byte[] SerializeObject<T>(T objectToSave)
{
byte[] convertedObject;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, objectToSave);
convertedObject = stream.ToArray();
}
return convertedObject;
}
}
从此 GUI 代码调用 AttemptSerializingData 时抛出错误:
using System.Collections.Generic;
using UnityEngine;
public class Entry : MonoBehaviour
{
private readonly List<string> logs = new List<string>();
private string a = "0", b = "5", c = "-2";
private bool displayProto;
private void OnGUI()
{
displayProto = GUILayout.Toggle(displayProto, "Display proto data");
a = GUILayout.TextField(a);
b = GUILayout.TextField(b);
c = GUILayout.TextField(c);
{
if (GUILayout.Button("Test int") && int.TryParse(a, out int _a) && int.TryParse(b, out int _b) && int.TryParse(c, out int _c))
{
logs.Clear();
var data = new DataDictionnary<string, int>();
data.AddValue("plop", _a);
data.AddValue("pop", _b);
data.AddValue("plp", _c);
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
}
if (GUILayout.Button("Test string"))
{
logs.Clear();
var data = new DataDictionnary<string, string>();
data.AddValue("plop", a);
data.AddValue("pop", b);
data.AddValue("plp", c);
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
{
if (GUILayout.Button("Test float") && float.TryParse(a, out float _a) && float.TryParse(b, out float _b) && float.TryParse(c, out float _c))
{
logs.Clear();
var data = new DataDictionnary<string, float>();
data.AddValue("plop", _a);
data.AddValue("pop", _b);
data.AddValue("plp", _c);
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
if (GUILayout.Button("Test int int"))
{
logs.Clear();
var data = new DataDictionnary<int, int>();
data.AddValue(0, 5);
data.AddValue(4, 41);
data.AddValue(2, 8);
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
if (GUILayout.Button("Test bool string"))
{
logs.Clear();
var data = new DataDictionnary<bool, string>();
data.AddValue(true, "ouyhgv");
data.AddValue(false, "hoipkip");
data.AddValue(false, "fguoii");
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
}
foreach (var log in logs)
{
GUILayout.Label(log);
}
}
public void AddLog(string text)
{
logs.Add(text);
}
}
我找到了解决方案 。使用该属性声明我的所有词典可以解决问题。
[ProtoMap(DisableMap = true)]
[ProtoMember(1)]
private readonly Dictionary<string, string> _stringData;
我正在尝试让 protobuf-net 与 Unity 一起工作。从我最近几天所做的大量研究来看,让两者一起正常工作似乎很复杂。在使用 IL2CPP 进行构建时,我现在遇到了一个无法解决的问题。
对于某些类型,序列化似乎有时会失败,例如 Dictionary
ExecutionEngineException: Attempting to call method 'ProtoBuf.Serializers.MapDecorator`3[[System.Collections.Generic.Dictionary`2[[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]::.cctor' for which no ahead of time (AOT) code was generated.
at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters) [0x00000] in <00000000000000000000000000000000>:0
at System.Reflection.MonoCMethod.DoInvoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0
at System.Reflection.MonoCMethod.Invoke (System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0
at System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.ValueMember.BuildSerializer () [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.ValueMember.get_Serializer () [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.MetaType.BuildSerializer () [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.MetaType.get_Serializer () [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.RuntimeTypeModel.Serialize (System.Int32 key, System.Object value, ProtoBuf.ProtoWriter dest) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.TypeModel.SerializeCore (ProtoBuf.ProtoWriter writer, System.Object value) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.TypeModel.Serialize (System.IO.Stream dest, System.Object value, ProtoBuf.SerializationContext context) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Meta.TypeModel.Serialize (System.IO.Stream dest, System.Object value) [0x00000] in <00000000000000000000000000000000>:0
at ProtoBuf.Serializer.Serialize[T] (System.IO.Stream destination, T instance) [0x00000] in <00000000000000000000000000000000>:0
at SerializationTests.SerializeObject[T] (T objectToSave) [0x00000] in <00000000000000000000000000000000>:0
at SerializationTests.AttemptSerializingData[T] (T dataToSerialize, Entry entry) [0x00000] in <00000000000000000000000000000000>:0
at Entry.OnGUI () [0x00000] in <00000000000000000000000000000000>:0
class 我正在尝试在副项目上进行序列化以进行测试,如下所示:
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
public class DataDictionnary<T, U>
{
[ProtoMember(1)] private readonly Dictionary<T, U> someDictionnary = new Dictionary<T, U>();
public DataDictionnary()
{
}
public void AddValue(T key, U value)
{
if (someDictionnary.ContainsKey(key)) return;
someDictionnary.Add(key, value);
}
public override string ToString()
{
string s = $"{GetType()}";
foreach (var key in someDictionnary.Keys)
{
s += $" ({key}, {someDictionnary[key]})";
}
return s;
}
}
有没有理由我只能用基元序列化某些类型的字典,我可以用来让它工作的任何东西?
编辑:我的 link.xml 文件应该可以防止代码剥离
<linker>
<assembly fullname="Test.Serialization" preserve="all"/>
<assembly fullname="protobuf-net" preserve="all"/>
</linker>
SerializationTests.cs
using System.IO;
using ProtoBuf;
public static class SerializationTests
{
public static void AttemptSerializingData<T>(T dataToSerialize, bool displayProto, Entry entry)
{
if (displayProto)
entry.AddLog(Serializer.GetProto<T>());
entry.AddLog($"Serializing {dataToSerialize}");
var serializedObject = SerializeObject(dataToSerialize);
entry.AddLog($"Done ({serializedObject.Length})");
var deserializedObject = DeserializeObject<T>(serializedObject);
entry.AddLog($"Done! {deserializedObject}");
}
private static T DeserializeObject<T>(byte[] bytes)
{
T result;
using (var stream = new MemoryStream(bytes))
{
result = Serializer.Deserialize<T>(stream);
}
return result;
}
private static byte[] SerializeObject<T>(T objectToSave)
{
byte[] convertedObject;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, objectToSave);
convertedObject = stream.ToArray();
}
return convertedObject;
}
}
从此 GUI 代码调用 AttemptSerializingData 时抛出错误:
using System.Collections.Generic;
using UnityEngine;
public class Entry : MonoBehaviour
{
private readonly List<string> logs = new List<string>();
private string a = "0", b = "5", c = "-2";
private bool displayProto;
private void OnGUI()
{
displayProto = GUILayout.Toggle(displayProto, "Display proto data");
a = GUILayout.TextField(a);
b = GUILayout.TextField(b);
c = GUILayout.TextField(c);
{
if (GUILayout.Button("Test int") && int.TryParse(a, out int _a) && int.TryParse(b, out int _b) && int.TryParse(c, out int _c))
{
logs.Clear();
var data = new DataDictionnary<string, int>();
data.AddValue("plop", _a);
data.AddValue("pop", _b);
data.AddValue("plp", _c);
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
}
if (GUILayout.Button("Test string"))
{
logs.Clear();
var data = new DataDictionnary<string, string>();
data.AddValue("plop", a);
data.AddValue("pop", b);
data.AddValue("plp", c);
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
{
if (GUILayout.Button("Test float") && float.TryParse(a, out float _a) && float.TryParse(b, out float _b) && float.TryParse(c, out float _c))
{
logs.Clear();
var data = new DataDictionnary<string, float>();
data.AddValue("plop", _a);
data.AddValue("pop", _b);
data.AddValue("plp", _c);
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
if (GUILayout.Button("Test int int"))
{
logs.Clear();
var data = new DataDictionnary<int, int>();
data.AddValue(0, 5);
data.AddValue(4, 41);
data.AddValue(2, 8);
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
if (GUILayout.Button("Test bool string"))
{
logs.Clear();
var data = new DataDictionnary<bool, string>();
data.AddValue(true, "ouyhgv");
data.AddValue(false, "hoipkip");
data.AddValue(false, "fguoii");
SerializationTests.AttemptSerializingData(data, displayProto, this);
}
}
foreach (var log in logs)
{
GUILayout.Label(log);
}
}
public void AddLog(string text)
{
logs.Add(text);
}
}
我找到了解决方案
[ProtoMap(DisableMap = true)]
[ProtoMember(1)]
private readonly Dictionary<string, string> _stringData;