如果不存在则序列化并跳过
Serialize and skip if doesn't exist
我将 Unity 与 GameSparks 结合使用,并有一个序列化程序脚本,可将 GameSparks 数据对象转换为 C# 模型。脚本如下所示:
public static object GSDataToObject(GSData gsData)
{
//Debug.Log("GSSerializer Return: \n"+gsData.JSON);
Type objType = Type.GetType(gsData.GetString("type"));
object obj = Activator.CreateInstance(objType);
foreach(var typeField in objType.GetFields())
{
if(!typeField.IsNotSerialized)
{
if(typeField.FieldType == typeof(string))
{
typeField.SetValue(obj, gsData.GetString(typeField.Name));
}
else if(typeField.FieldType == typeof(int))
{
typeField.SetValue(obj, (int)gsData.GetNumber(typeField.Name).Value);
}
else if(typeField.FieldType == typeof(float))
{
typeField.SetValue(obj, (float)gsData.GetFloat(typeField.Name).Value);
}
else if(typeField.FieldType == typeof(bool))
{
typeField.SetValue(obj, gsData.GetBoolean(typeField.Name));
}
else if(typeField.FieldType == typeof(DateTime))
{
typeField.SetValue(obj, gsData.GetDate(typeField.Name));
}
else if((typeField.FieldType == typeof(List<string>) || typeField.FieldType == typeof(string[]) ))
{
typeField.SetValue(obj, (typeField.FieldType == typeof(List<string>)) ? (object)gsData.GetStringList(typeField.Name) : gsData.GetStringList(typeField.Name).ToArray());
}
else if((typeField.FieldType == typeof(List<int>) || typeField.FieldType == typeof(int[])) )
{
typeField.SetValue(obj, (typeField.FieldType == typeof(List<int>)) ? (object)gsData.GetIntList(typeField.Name) : gsData.GetIntList(typeField.Name).ToArray());
}
else if((typeField.FieldType == typeof(List<float>) || typeField.FieldType == typeof(float[])) )
{
typeField.SetValue(obj, (typeField.FieldType == typeof(List<float>)) ? (object)gsData.GetFloatList(typeField.Name) : gsData.GetFloatList(typeField.Name).ToArray());
}
else if(typeField.FieldType.IsClass && !typeField.FieldType.IsGenericType && !typeField.FieldType.IsArray)
{
typeField.SetValue(obj, GSDataToObject(gsData.GetGSData(typeField.Name)));
}
else if(!typeField.FieldType.IsArray && typeof(IList).IsAssignableFrom(typeField.FieldType))
{
IList genericList = Activator.CreateInstance(typeField.FieldType) as IList;
foreach(GSData gsDataElem in gsData.GetGSDataList(typeField.Name))
{
object elem = GSDataToObject(gsDataElem);
genericList.Add(elem);
}
typeField.SetValue(obj, genericList);
}
else if(typeField.FieldType.IsArray)
{
List<GSData> gsArrayData = gsData.GetGSDataList(typeField.Name);
// create a new instance of the array. The Activator class cannot do this for arrays //
// so this will create a new array of types inside the array, with the count of what is in the gsdata list //
Array newArray = Array.CreateInstance(typeField.FieldType.GetElementType(), gsArrayData.Count);
object[] objArray = new object[gsArrayData.Count]; // create a new array of objects where the serialized objects will be kept
for(int i = 0; i < gsArrayData.Count; i++)
{
objArray[i] = GSDataToObject(gsArrayData[i]); // convert the JSON data inside the list to an object
}
Array.Copy(objArray, newArray, objArray.Length); //covert the object[] to the original type
typeField.SetValue(obj, newArray);
}
}
}
return obj;
}
我的模型是这样的:
[System.Serializable]
public class UserData
{
public string name;
public string email;
}
然后,当我从 GameSparks 获取数据时调用脚本,如下所示:
UserData uData = new UserData();
uData = GameSparksSerialiser.GSDataToObject(response.ScriptData.GetGSData("@userReturn")) as UserData;
现在,所有这一切都很完美,但如果我决定从现在开始在所有新创建的文档 (mongodb) 中添加一个新字段,我就会遇到问题。我在 Unity 中将新字段添加到我的模型中,如下所示:
[System.Serializable]
public class UserData
{
public string name;
public string email;
public bool isAdmin;
}
这对所有新文档都没有错误,但是当尝试序列化旧文档时,我得到这个错误:
System.InvalidOperationException: Nullable object must have a value.
现在,我怎样才能避免这种情况,使用序列化程序脚本...是否可以不出错,这样我就不必更改所有旧文档?
希望得到帮助并提前致谢:-)
不知道 GSData
是什么或吸气剂是如何实现的,但问题似乎来自例如(如果包含完整的错误消息,将会有所帮助)
typeField.SetValue(obj, gsData.GetBoolean(typeField.Name));
您正在尝试获取以前不存在的字段的值。
您应该将其包装在 try-catch
块中。为了不必为每种情况都这样做,您应该包装整个
foreach(var typeField in objType.GetFields())
{
try{
if(!typeField.IsNotSerialized)
{
.......
}
}
catch(Exception e)
{
Debug.LogWarning($"{e.GetType} while trying to get value for {typeField.Name}: {e.Message}\n{e.StackTrace}");
}
}
这仍然会在控制台中抛出错误,但不像异常通常会破坏您的应用程序/for 循环,而是简单地继续其余部分。
一般旁注:
这样做是多余的
UserData uData = new UserData();
就在使用前
uData = GameSparksSerialiser.GSDataToObject(response.ScriptData.GetGSData("@userReturn")) as UserData;
这只为 GarbageCollector 创建了一些工作,用于销毁由 new
创建的 UserData
这个未使用的实例。直接用
UserData uData = GameSparksSerialiser.GSDataToObject(response.ScriptData.GetGSData("@userReturn")) as UserData;
我将 Unity 与 GameSparks 结合使用,并有一个序列化程序脚本,可将 GameSparks 数据对象转换为 C# 模型。脚本如下所示:
public static object GSDataToObject(GSData gsData)
{
//Debug.Log("GSSerializer Return: \n"+gsData.JSON);
Type objType = Type.GetType(gsData.GetString("type"));
object obj = Activator.CreateInstance(objType);
foreach(var typeField in objType.GetFields())
{
if(!typeField.IsNotSerialized)
{
if(typeField.FieldType == typeof(string))
{
typeField.SetValue(obj, gsData.GetString(typeField.Name));
}
else if(typeField.FieldType == typeof(int))
{
typeField.SetValue(obj, (int)gsData.GetNumber(typeField.Name).Value);
}
else if(typeField.FieldType == typeof(float))
{
typeField.SetValue(obj, (float)gsData.GetFloat(typeField.Name).Value);
}
else if(typeField.FieldType == typeof(bool))
{
typeField.SetValue(obj, gsData.GetBoolean(typeField.Name));
}
else if(typeField.FieldType == typeof(DateTime))
{
typeField.SetValue(obj, gsData.GetDate(typeField.Name));
}
else if((typeField.FieldType == typeof(List<string>) || typeField.FieldType == typeof(string[]) ))
{
typeField.SetValue(obj, (typeField.FieldType == typeof(List<string>)) ? (object)gsData.GetStringList(typeField.Name) : gsData.GetStringList(typeField.Name).ToArray());
}
else if((typeField.FieldType == typeof(List<int>) || typeField.FieldType == typeof(int[])) )
{
typeField.SetValue(obj, (typeField.FieldType == typeof(List<int>)) ? (object)gsData.GetIntList(typeField.Name) : gsData.GetIntList(typeField.Name).ToArray());
}
else if((typeField.FieldType == typeof(List<float>) || typeField.FieldType == typeof(float[])) )
{
typeField.SetValue(obj, (typeField.FieldType == typeof(List<float>)) ? (object)gsData.GetFloatList(typeField.Name) : gsData.GetFloatList(typeField.Name).ToArray());
}
else if(typeField.FieldType.IsClass && !typeField.FieldType.IsGenericType && !typeField.FieldType.IsArray)
{
typeField.SetValue(obj, GSDataToObject(gsData.GetGSData(typeField.Name)));
}
else if(!typeField.FieldType.IsArray && typeof(IList).IsAssignableFrom(typeField.FieldType))
{
IList genericList = Activator.CreateInstance(typeField.FieldType) as IList;
foreach(GSData gsDataElem in gsData.GetGSDataList(typeField.Name))
{
object elem = GSDataToObject(gsDataElem);
genericList.Add(elem);
}
typeField.SetValue(obj, genericList);
}
else if(typeField.FieldType.IsArray)
{
List<GSData> gsArrayData = gsData.GetGSDataList(typeField.Name);
// create a new instance of the array. The Activator class cannot do this for arrays //
// so this will create a new array of types inside the array, with the count of what is in the gsdata list //
Array newArray = Array.CreateInstance(typeField.FieldType.GetElementType(), gsArrayData.Count);
object[] objArray = new object[gsArrayData.Count]; // create a new array of objects where the serialized objects will be kept
for(int i = 0; i < gsArrayData.Count; i++)
{
objArray[i] = GSDataToObject(gsArrayData[i]); // convert the JSON data inside the list to an object
}
Array.Copy(objArray, newArray, objArray.Length); //covert the object[] to the original type
typeField.SetValue(obj, newArray);
}
}
}
return obj;
}
我的模型是这样的:
[System.Serializable]
public class UserData
{
public string name;
public string email;
}
然后,当我从 GameSparks 获取数据时调用脚本,如下所示:
UserData uData = new UserData();
uData = GameSparksSerialiser.GSDataToObject(response.ScriptData.GetGSData("@userReturn")) as UserData;
现在,所有这一切都很完美,但如果我决定从现在开始在所有新创建的文档 (mongodb) 中添加一个新字段,我就会遇到问题。我在 Unity 中将新字段添加到我的模型中,如下所示:
[System.Serializable]
public class UserData
{
public string name;
public string email;
public bool isAdmin;
}
这对所有新文档都没有错误,但是当尝试序列化旧文档时,我得到这个错误:
System.InvalidOperationException: Nullable object must have a value.
现在,我怎样才能避免这种情况,使用序列化程序脚本...是否可以不出错,这样我就不必更改所有旧文档?
希望得到帮助并提前致谢:-)
不知道 GSData
是什么或吸气剂是如何实现的,但问题似乎来自例如(如果包含完整的错误消息,将会有所帮助)
typeField.SetValue(obj, gsData.GetBoolean(typeField.Name));
您正在尝试获取以前不存在的字段的值。
您应该将其包装在 try-catch
块中。为了不必为每种情况都这样做,您应该包装整个
foreach(var typeField in objType.GetFields())
{
try{
if(!typeField.IsNotSerialized)
{
.......
}
}
catch(Exception e)
{
Debug.LogWarning($"{e.GetType} while trying to get value for {typeField.Name}: {e.Message}\n{e.StackTrace}");
}
}
这仍然会在控制台中抛出错误,但不像异常通常会破坏您的应用程序/for 循环,而是简单地继续其余部分。
一般旁注:
这样做是多余的
UserData uData = new UserData();
就在使用前
uData = GameSparksSerialiser.GSDataToObject(response.ScriptData.GetGSData("@userReturn")) as UserData;
这只为 GarbageCollector 创建了一些工作,用于销毁由 new
创建的 UserData
这个未使用的实例。直接用
UserData uData = GameSparksSerialiser.GSDataToObject(response.ScriptData.GetGSData("@userReturn")) as UserData;