如何解决错误:在解析完成之前遇到流结束,在 for 循环中使用 BinaryFormatter.Deserialize
当我使用在 for 循环中使用 JsonUltility 的加载系统时遇到问题,它会引发如下错误:
“在解析完成之前遇到流结束”
保存和加载系统的代码是这样的:
类保存和加载系统的完整示例:
public void SaveParty(Player[] players,string nameFile)
{
Debug.Log("Saving!");
FileStream file = new FileStream(Application.persistentDataPath + nameFile,FileMode.OpenorCreate);
try
{
// Binary Formatter
BinaryFormatter formatter = new BinaryFormatter();
// Serialize
for(int x = 0; x < players.Length; x++)
{
var json = JsonUtility.ToJson(players[x]);
formatter.Serialize(file,json);
}
}
catch (SerializationException e)
{
Debug.LogError("Error Saving: " + e.Message);
}
finally
{
file.Close();
Debug.Log(Application.persistentDataPath);
}
}
// Save Current Party Data
public void SaveCurrentParty()
{
// If exist will delete,and save again
if(FirstTimeSaving)
{
File.Delete(Application.persistentDataPath + NameFile[1]);
SaveParty(gameManager.CurrentParty,NameFile[1]);
}
// If not exist will save
if(!FirstTimeSaving)
{
SaveParty(gameManager.CurrentParty,NameFile[1]);
FirstTimeSaving = true;
}
}
// Load Party Data
public void LoadParty(Player[] party,string nameFile)
{
Debug.Log("Loading!");
FileStream file = new FileStream(Application.persistentDataPath + nameFile,FileMode.Open);
try
{
BinaryFormatter formatter = new BinaryFormatter();
// Error Here ↓
for(int i = 0; i < party.Length; i++)
{
JsonUtility.FromJsonOverwrite((string)formatter.Deserialize(file),party[i]);
}
}
catch (SerializationException e)
{
Debug.Log("Error Load: " + e.Message);
}
finally
{
file.Close();
}
}
而且我知道错误是在 LoadParty 中使用 JsonUtility 进行循环。
这就是我要保存的内容:
[System.Serializable]
public class Player : MonoBehavIoUr
{
// Stats
public int Hp;
public int Sp;
public int level;
public int St;
public int Ma;
public int Ag;
public int En;
public int Lu;
// Items
public MeleeWeapon Weapon;
// Skills
public List<Skills> skills = new List<Skills>();
}
解决方法
您试图在循环中重复读取同一个 FileStream
。第一个操作读取文件并将位置前进到流的末尾。之后抛出异常,因为流中没有任何东西可供读取。
这发生在这里 - 它与 JsonUtility
无关:
(string)formatter.Deserialize(file)
读取整个流到最后。
这在您编写时不是问题,因为对于您序列化的每个对象,您都将附加到流中。
一种解决方案是序列化和反序列化整个 Player[]
。这样你只能从流中读取一次。当您打开文件时,您位于位置 0 - 流的开头。您读取整个文件并反序列化 Player[]
,而不是单个 Player
对象。完成后,您已阅读整个流并拥有所有数据。
我正在回避是否甚至使用二进制序列化的担忧。无论数据如何序列化,基本原则都适用。
,一般来说 Don't use BinaryFormatter anymore at all! .. 就您而言,您已经拥有一个序列化的 JSON string
... 为什么要在其上使用 BinaryFormatter
?
首先我会为玩家创建一个合适的数据容器类
[Serializable]
public class PlayerStats
{
public int Hp;
public int Sp;
public int level;
public int St;
public int Ma;
public int Ag;
public int En;
public int Lu;
// Assuming here that both MeleeWepaon and Skills are Serializable types
public MeleeWeapon Weapon;
// Skills
public List<Skills> skills = new List<Skills>();
}
public class Player : MonoBehaviour
{
public PlayerStats Stats;
}
为什么? -> 因为不允许通过从 JSON 反序列化来创建 MonoBehaviour
实例,这是我们将在下面进一步做的事情 ;)
然后简单地使用一个包装类,例如
using System.Linq;
[Serializable]
public class PlayersData
{
public PlayerStats[] Data;
// Empty constructor is needed by serializer
public PlayersData(){}
public PlayersData(IEnumarable<Player> data)
{
Data = data.Select(p => p.Stats).ToArray();
}
}
为什么? -> 内置的 JsonUtility
不支持数组/列表类型的直接(反)序列化。
所以改为序列化这个类型,然后直接将生成的 JSON 字符串写入文件中,例如
public void SaveParty(Player[] players,string nameFile)
{
var path = Path.Combine(Application.persistentDataPath,nameFile);
Debug.Log($"Saving!");
try
{
var json = JsonUtility.ToJson(new PlayersData (players));
File.WriteAllText(path,json);
Debug.Log($"Saved to \"{path}\n");
}
catch (Exception e)
{
Debug.LogError($"{e.GetType} while saving: {e.Message}\n{e.StackTrace}");
}
}
对于加载只是反过来,只需加载整个内容并将其反序列化到包装容器类中即可。然后将数据分配给相应的玩家实例
public void LoadParty(Player[] party,string nameFile)
{
Debug.Log("Loading!");
var path = Path.Combine(Application.persistentDataPath,nameFile);
try
{
var json = File.ReadAllText(path);
var data = JsonUtility.FromJson<PlayersData>(JSON);
for(var i = 0; i < Mathf.Min(data.Length,party.Length); i++)
{
party[i].Stats = data.Data[i];
}
}
catch (Exception e)
{
Debug.LogError($"{e.GetType} while loading: {e.Message}\n{e.StackTrace}");
}
}
提示:如果您的 Player
有任何您想要(反)序列化的 [SerializeField] private ...
字段,那么您宁愿必须在 Player
中实现一个 setter 方法PlayerStats
参数并相应地“手动”分配它们
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。