Unity Error: "End of Stream encountered before parsing was completed" with an own save and load system
Unity Error: "End of Stream encountered before parsing was completed" with an own save and load system
问题
你好,我有一个问题:当我使用 LoadParty 方法时,它在 for 循环中使用 JsonUltility,它只是抛出一个错误:
"End of Stream encountered before parsing was completed"
for 循环沿着名为 Player 的 class 运行,其中包含玩家的所有统计数据。所以我认为问题可能与此有关。
我试过的
我试图对 for 循环进行更改,以便程序可以反序列化数据,但我无法找到解决方案。
代码
Class 保存和加载系统的完整代码:
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();
}
}
播放器的完整代码class:
[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
就可以了吗?
首先我会为像
这样的玩家创建一个合适的数据容器class
[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
实例,这就是我们将在下面进一步做的事情;)
然后简单地使用包装器class,例如
using System.Linq;
[Serializable]
public class PlayersData
{
public PlayerStats[] Data;
// Empty constructor is needed by serializer
public PlayersData(){}
public PlayersData(IEnumerable<Player> data)
{
Data = data.Select(p => p.Stats).ToArray();
}
}
为什么? -> 内置 JsonUtility
不支持 array/list 类型的直接(反)序列化。
因此改为序列化此类型,然后直接将生成的 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}");
}
}
而对于加载,则相反,只需加载整个内容并将其反序列化到包装容器 class 中。然后将数据分配给相应的播放器实例
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
获取 PlayerStats
参数并相应地“手动”分配它们
问题
你好,我有一个问题:当我使用 LoadParty 方法时,它在 for 循环中使用 JsonUltility,它只是抛出一个错误:
"End of Stream encountered before parsing was completed"
for 循环沿着名为 Player 的 class 运行,其中包含玩家的所有统计数据。所以我认为问题可能与此有关。
我试过的
我试图对 for 循环进行更改,以便程序可以反序列化数据,但我无法找到解决方案。
代码
Class 保存和加载系统的完整代码:
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();
}
}
播放器的完整代码class:
[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
就可以了吗?
首先我会为像
这样的玩家创建一个合适的数据容器class[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
实例,这就是我们将在下面进一步做的事情;)
然后简单地使用包装器class,例如
using System.Linq;
[Serializable]
public class PlayersData
{
public PlayerStats[] Data;
// Empty constructor is needed by serializer
public PlayersData(){}
public PlayersData(IEnumerable<Player> data)
{
Data = data.Select(p => p.Stats).ToArray();
}
}
为什么? -> 内置 JsonUtility
不支持 array/list 类型的直接(反)序列化。
因此改为序列化此类型,然后直接将生成的 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}");
}
}
而对于加载,则相反,只需加载整个内容并将其反序列化到包装容器 class 中。然后将数据分配给相应的播放器实例
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
获取 PlayerStats
参数并相应地“手动”分配它们