c# - 如果不存在,则序列化并跳过
问题描述
我将 Unity 与 GameSparks 一起使用,并且有一个序列化程序脚本,它将 GameSparks 数据对象转换为 ac# 模型。脚本如下所示:
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
getter 是什么或如何实现的,但问题似乎来自例如(如果您包含完整的错误消息会有所帮助)
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 创建一些工作,用于销毁这个未使用的UserData
create by实例new
。只需直接使用
UserData uData = GameSparksSerialiser.GSDataToObject(response.ScriptData.GetGSData("@userReturn")) as UserData;
推荐阅读
- html - MODX 将 RESOURCES 获取为 html
- javascript - 本地范围调用如何在其自身功能之外工作?- jQuery
- powershell - 尝试使用 invoke-command、chocolatey 和 7zip 解压缩文件
- javascript - 在Vue中更改对象时不呈现div
- c# - 如何使用 C# 比较两个图像并获得透明背景图像?
- arrays - 使用 Node Js 和 ForEach 循环在 cloudinary 中多次上传
- linux - 如何改变语音共振峰?
- python - 可以在 Jupyter 服务器中使用 tkinter GUI 吗?
- swift - 如何在 Swift 程序中更新 SQLite 表中的列?
- linux - Bash 脚本,读取超时搞砸了提示