无法从XML反序列化对象集合——游戏存档加载报错求助
游戏存读档系统反序列化XmlException问题排查
我正在为游戏开发一套存读档系统,目标是将Dictionary<string, object>转换为加密字符串写入磁盘,并能反向读取还原。保存流程(获取可存档数据、序列化、加密、写入磁盘)完全顺畅,但加载时的反序列化阶段抛出异常,无法正确还原状态字典。
保存相关代码
Save方法与数据准备
public void Save() { PrepareSavableData(); using StreamWriter writer = new(SavegamePath); string rawData = AuxMath.SerializeObjectToString(_storedStates); string encodedData = AuxMath.Encode(rawData, PublicKey, PrivateKey); writer.Write(encodedData); writer.Close(); SavegameCompleted?.Invoke(this, EventArgs.Empty); } private void PrepareSavableData() { foreach (var entity in _registeredEntities) { _storedStates[entity.ID] = entity.GetState(); } }
获取实体状态
public object GetState() { List<object> states = new(_savables.Count); foreach (var savable in _savables) { states.Add(savable.CaptureState()); } return states; }
序列化与加密辅助方法
public static string SerializeObjectToString(object obj) { using MemoryStream memoryStream = new(); using StreamReader reader = new(memoryStream, Encoding.UTF8); DataContractSerializer serializer = new(obj.GetType()); serializer.WriteObject(memoryStream, obj); memoryStream.Position = 0; return reader.ReadToEnd(); } public static string Encode(string input, string publicKey, string privateKey) { try { byte[] inputAsBytes = Encoding.UTF8.GetBytes(input); byte[] publicKeyAsBytes = Encoding.UTF8.GetBytes(publicKey); byte[] privateKeyAsBytes = Encoding.UTF8.GetBytes(privateKey); using DESCryptoServiceProvider cryptoServiceProvider = new(); using MemoryStream memoryStream = new(); using CryptoStream cryptoStream = new(memoryStream, cryptoServiceProvider.CreateEncryptor(publicKeyAsBytes, privateKeyAsBytes), CryptoStreamMode.Write); cryptoStream.Write(inputAsBytes, 0, inputAsBytes.Length); cryptoStream.FlushFinalBlock(); return Convert.ToBase64String(memoryStream.ToArray()); } catch (Exception e) { return e.Message; } }
加载相关代码
Load方法与解密
private bool Load() { if (File.Exists(SavegamePath)) { try { using StreamReader reader = new(SavegamePath); string encodedData = reader.ReadToEnd(); string decodedData = AuxMath.Decode(encodedData, PublicKey, PrivateKey); _storedStates = AuxMath.DeserializeStringToObject<Dictionary<string, object>>(decodedData, _knownSavableTypes); SavegameLoadSuccesful?.Invoke(this, EventArgs.Empty); return true; } catch (Exception e) { SavegameLoadFailed?.Invoke(this, new SavegameLoadFailedEventArgs(e.Message)); return false; } } return false; } public static string Decode(string encodedInput, string publicKey, string privateKey) { try { byte[] encodedInputAsBytes = Convert.FromBase64String(encodedInput); byte[] publicKeyAsBytes = Encoding.UTF8.GetBytes(publicKey); byte[] privateKeyAsBytes = Encoding.UTF8.GetBytes(privateKey); using DESCryptoServiceProvider cryptoServiceProvider = new(); using MemoryStream memoryStream = new(); using CryptoStream cryptoStream = new(memoryStream, cryptoServiceProvider.CreateDecryptor(publicKeyAsBytes, privateKeyAsBytes), CryptoStreamMode.Write); cryptoStream.Write(encodedInputAsBytes, 0, encodedInputAsBytes.Length); cryptoStream.FlushFinalBlock(); return Encoding.UTF8.GetString(memoryStream.ToArray()); } catch (Exception e) { return e.Message; } }
出问题的反序列化方法
public static T DeserializeStringToObject<T>(string objectAsXml, IEnumerable<Type> knownTypes) { using MemoryStream memoryStream = new(); using StreamWriter writer = new(memoryStream, Encoding.UTF8); DataContractSerializer deserializer = new(typeof(T), knownTypes); writer.Write(objectAsXml); memoryStream.Position = 0; if (deserializer.ReadObject(memoryStream) is T value) { return value; } else { throw new Exception("Passed data is invalid or corrupted and cannot be properly restored!"); } }
错误信息
XmlException: Unexpected end of file. Following elements are not closed: Value, KeyValueOfstringanyType, ArrayOfKeyValueOfstringanyType. Line 1, position 197.
问题原因与修复方案
核心原因
StreamWriter内部存在缓冲区,调用Write方法后,字符串数据并未立即全部写入MemoryStream,此时直接设置memoryStream.Position = 0并读取,只能获取到部分XML内容,导致XML结构不完整,触发未结束的异常。
修复方案1:刷新StreamWriter缓冲区
在写入字符串后,调用writer.Flush()强制将缓冲区数据写入内存流:
public static T DeserializeStringToObject<T>(string objectAsXml, IEnumerable<Type> knownTypes) { using MemoryStream memoryStream = new(); using StreamWriter writer = new(memoryStream, Encoding.UTF8); DataContractSerializer deserializer = new(typeof(T), knownTypes); writer.Write(objectAsXml); writer.Flush(); // 强制刷新缓冲区 memoryStream.Position = 0; if (deserializer.ReadObject(memoryStream) is T value) { return value; } else { throw new Exception("Passed data is invalid or corrupted and cannot be properly restored!"); } }
修复方案2:直接将字符串转为字节数组写入内存流
绕过StreamWriter的缓冲机制,直接将字符串编码为字节数组初始化MemoryStream:
public static T DeserializeStringToObject<T>(string objectAsXml, IEnumerable<Type> knownTypes) { byte[] xmlBytes = Encoding.UTF8.GetBytes(objectAsXml); using MemoryStream memoryStream = new(xmlBytes); DataContractSerializer deserializer = new(typeof(T), knownTypes); if (deserializer.ReadObject(memoryStream) is T value) { return value; } else { throw new Exception("Passed data is invalid or corrupted and cannot be properly restored!"); } }
额外验证建议
在Load方法中输出decodedData的内容或长度,确认解密后的XML字符串是完整的,排除解密过程中数据丢失的可能性。
内容的提问来源于stack exchange,提问作者Heavy Violence
相关产品推荐
相关产品推荐

