“InvalidCastException:无法从源类型转换为目标类型。”加载序列化数据时

时间:2021-04-30 15:38:39

I'd sincerely appreciate if you looked through some of my code as I can't seem to be able to fix it myself, and this will also hopefully help others understand serialization better.

如果你查看了我的一些代码,我真的很感激,因为我似乎无法自己修复它,这也有希望帮助其他人更好地理解序列化。

I have four pieces of related code here, with anything seemingly irrelevant cut out. I'm trying to create a user profile in a set up where the user then chooses from a range of games to play. Only options/settings for the chosen games, in this case something to do with a fish, are saved.

我在这里有四个相关的代码,其中任何看似无关紧要的东西都被切掉了。我正在尝试在设置中创建用户个人资料,然后用户可以从一系列游戏中进行选择。仅保存所选游戏的选项/设置,在这种情况下与鱼有关。

The error in the title occurs in the 4th section [feel free to skip to that] which is the OptionsController.LoadOptions() function at

标题中的错误发生在第4节[随意跳到那个],这是OptionsController.LoadOptions()函数在

FishData.Store data = (FishData.Store)bf.Deserialize(fileOpt);

I have this at the top of all following sections:

我把它放在以下所有部分的顶部:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

1) Profile data structure:

1)配置文件数据结构:

public class Profile {                                   //Profile variables

    public static string id;
    public static string name;
    public static string dob;
    public static bool fishEnabled;
    public static bool birdEnabled;

    public static string stringTest;


    [System.Serializable]
    public class Store {                                   //Profile variables

        public string id;
        public string name;
        public string dob;
        public bool fishEnabled;
        public bool birdEnabled;

        public string stringTest;

    }   
}

2) Fish data structure:

2)鱼类数据结构:

 public class FishData {                                   //static Fish variables to be used in game

    public static bool sizeInc;
    public static bool speedInc;
    public static int level;
    public static bool toxic;
    public static bool handLeft;

    public static bool timer;
    public static int timerVal;
    public static int percentReq;
    public static int successReq;

    public static string testStringTwo;

    public static List<StatController.Session> sessions = new List<StatController.Session>();


    [System.Serializable]
    public class Store {                               //non static variable set for serialization

        public bool sizeInc;
        public bool speedInc;
        public int level;
        public bool toxic;
        public bool handLeft;

        public bool timer;
        public int timerVal;
        public int percentReq;
        public int successReq;

        public string testStringTwo;

        public List<StatController.Session> sessions = new List<StatController.Session>();
    }

}

3) The script where the data file was initially created and where there didn't seem to be problems:

3)最初创建数据文件的脚本以及似乎没有问题的地方:

public class ProfileController : MonoBehaviour {


    public static string currentID;

    //create ----
    public InputField idField;
    public InputField nameField;
    public InputField dobField;
    public Toggle fishToggle;
    public Toggle birdToggle;

    //open ------
    public Button idTitleButton;
    public Text nameText;
    public Text dobText;

    public Text testText;
    public InputField numField;



    void Save() { //saves new ID and declares required variables based on game choice

        Debug.Log("id =" + idField.text + ", name = " + nameField.text + ", save");
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Create(Application.persistentDataPath + "/" + idField.text + ".dat");

        Profile.Store data = new Profile.Store();                 //sets Profile variables

        currentID = idField.text;

        data.id = idField.text;
        data.name = nameField.text;
        data.dob = dobField.text;
        data.fishEnabled = fishToggle.isOn;
        data.birdEnabled = birdToggle.isOn;

        Profile.id = idField.text;
        Profile.name = nameField.text;
        Profile.dob = dobField.text;
        Profile.fishEnabled = fishToggle.isOn;
        Profile.birdEnabled = birdToggle.isOn;



        bf.Serialize(file, data);                            //saves Profile variables

        if (fishToggle.isOn) {
            FishData.Store dataFish = new FishData.Store();              //sets default Fish variables

            dataFish.sizeInc = false;
            dataFish.speedInc = false;
            dataFish.level = 5;
            dataFish.toxic = true;
            dataFish.handLeft = false;

            dataFish.timer = false;
            dataFish.timerVal = 240;
            dataFish.percentReq = 0;
            dataFish.successReq = 0;

            bf.Serialize(file, dataFish);                   //saves default Fish variables

            Debug.Log("level = " + dataFish.level);



        }

        file.Close();                                         //closes save file

        idSelectField.text = idField.text;          //ensures current ID is used as selected ID where needed
        EnableOpenProfile();                                   //loads new profile

    }


    void Load() {


        if (File.Exists(Application.persistentDataPath + "/" + idField.text + ".dat")) {  //loads save file
            currentID = idField.text;
            BinaryFormatter bf = new BinaryFormatter();
            FileStream file = File.Open(Application.persistentDataPath + "/" + idField.text + ".dat", FileMode.Open);
            Profile.Store data = (Profile.Store)bf.Deserialize(file);

            nameField.text = data.name;                             //loads saved Profile details and settings
            fishToggle.isOn = data.fishEnabled;
            birdToggle.isOn = data.birdEnabled;

            fishPlayButton.enabled = data.fishEnabled;
            birdPlayButton.enabled = data.birdEnabled;


            FishData.Store dataFish = (FishData.Store)bf.Deserialize(file);     //loads saved Fish settings
            FishData.sizeInc = dataFish.sizeInc;
            FishData.speedInc = dataFish.speedInc;
            FishData.toxic = dataFish.toxic;
            FishData.timer = dataFish.timer;
            FishData.level = dataFish.level;
            FishData.timerVal = dataFish.timerVal;
            FishData.percentReq = dataFish.percentReq;
            FishData.successReq = dataFish.successReq;
            FishData.handLeft = dataFish.handLeft;



            file.Close();                                           //closes save file


            nameText.text = "Name : " + data.name + "     ID : " + data.id;     //displays profile details
            dobText.text = "DOB : " + data.dob;


        }
        else return;
    }

}

4) The main script that produces the error when trying to load what was saved in the previous script:

4)尝试加载上一个脚本中保存的内容时产生错误的主脚本:

    public class OptionsController : MonoBehaviour {

    public static bool handLeft = false;     

    public Toggle sizeIncToggle;
    public Toggle speedIncToggle;
    public Toggle toxicToggle;
    public Toggle timerToggle;

    public Toggle leftToggle;
    public Toggle rightToggle;

    public InputField levelField;
    public InputField timerValField;
    public InputField percentReqField;
    public InputField successReqField;


    void LoadOptions() {

        if (File.Exists(Application.persistentDataPath + "/" + ProfileController.currentID + ".dat")) {
            BinaryFormatter bf = new BinaryFormatter();
            FileStream fileOpt = File.Open(Application.persistentDataPath + "/" + ProfileController.currentID + ".dat", FileMode.Open);

            FishData.Store data = (FishData.Store)bf.Deserialize(fileOpt); //[[ERROR HERE]]

            sizeIncToggle.isOn = data.sizeInc;
            speedIncToggle.isOn = data.speedInc;
            toxicToggle.isOn = data.toxic;
            timerToggle.isOn = data.timer;
            levelField.text = data.level.ToString();
            timerValField.text = data.timerVal.ToString();
            percentReqField.text = data.percentReq.ToString();
            successReqField.text = data.successReq.ToString();

            fileOpt.Close();

        }
    }



    public void SaveOptions() {

        Debug.Log("name = " + ProfileController.currentID + ", save");
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Create(Application.persistentDataPath + "/" + ProfileController.currentID + ".dat");

        FishData.Store data = new FishData.Store();

        data.sizeInc = FishData.sizeInc;
        data.speedInc = FishData.speedInc;
        data.toxic = FishData.toxic;
        data.timer = FishData.timer;
        data.level = FishData.level;
        data.timerVal = FishData.timerVal;
        data.percentReq = FishData.percentReq;
        data.successReq = FishData.successReq;
        data.handLeft = FishData.handLeft;


        bf.Serialize(file, data);

        Debug.Log("level = " + FishData.level);

        file.Close();

    }

}

Thank you.

2 个解决方案

#1


To ensure you are serializing/deserializing in the correct order it is best that you create a "holding" class which contains both a Fish.Store variable and Profile.Store variable. If you serialize and deserilize in different orders you will run into issues like trying to deserialize a Fish.Store object to Profile.Store.

为确保以正确的顺序序列化/反序列化,最好创建一个包含Fish.Store变量和Profile.Store变量的“holding”类。如果您按照不同的顺序进行序列化和deserilize,则会遇到类似尝试将Fish.Store对象反序列化为Profile.Store的问题。

Try creating another class with public variables of Fish.Store and Profile.Store, like:

尝试使用Fish.Store和Profile.Store的公共变量创建另一个类,如:

public class ExampleClass
{
    public Fish.Store FishStore;
    public Profile.Store ProfileStore;

    public ExampleClass()
    {
    }
}

Then do:

ExampleClass example = new ExampleClass();
example.ProfileStore = data;
example.FishStore = dataFish;

//File Create stuff, etc
bf.Serialize(file, example);
//File close stuff, etc

And to deserialize:

并反序列化:

ExampleClass e = (ExampleClass)bf.Deserialize(fileOpt);
data = e.ProfileStore;
dataFish = e.FishStore;

If you only wish to serialize/deserialize one class at a time, it is best practice to read/write to/from one file per serialized class.

如果您只希望一次序列化/反序列化一个类,则最佳做法是对每个序列化类的一个文件进行读/写。

#2


Correct me if I'm wrong but it seems you first save Profile.Store and then you save FishData.Store.

纠正我,如果我错了,但似乎你首先保存Profile.Store然后你保存FishData.Store。

Then on the load you try to retreive the FishData.Store when the Profile.Store is first in the file.

然后在加载时,当Profile.Store在文件中的第一个时,您尝试检索FishData.Store。

(I'm assuming you use the load from script 4 and the save in script 3.)

(我假设您使用脚本4中的加载和脚本3中的保存。)

#1


To ensure you are serializing/deserializing in the correct order it is best that you create a "holding" class which contains both a Fish.Store variable and Profile.Store variable. If you serialize and deserilize in different orders you will run into issues like trying to deserialize a Fish.Store object to Profile.Store.

为确保以正确的顺序序列化/反序列化,最好创建一个包含Fish.Store变量和Profile.Store变量的“holding”类。如果您按照不同的顺序进行序列化和deserilize,则会遇到类似尝试将Fish.Store对象反序列化为Profile.Store的问题。

Try creating another class with public variables of Fish.Store and Profile.Store, like:

尝试使用Fish.Store和Profile.Store的公共变量创建另一个类,如:

public class ExampleClass
{
    public Fish.Store FishStore;
    public Profile.Store ProfileStore;

    public ExampleClass()
    {
    }
}

Then do:

ExampleClass example = new ExampleClass();
example.ProfileStore = data;
example.FishStore = dataFish;

//File Create stuff, etc
bf.Serialize(file, example);
//File close stuff, etc

And to deserialize:

并反序列化:

ExampleClass e = (ExampleClass)bf.Deserialize(fileOpt);
data = e.ProfileStore;
dataFish = e.FishStore;

If you only wish to serialize/deserialize one class at a time, it is best practice to read/write to/from one file per serialized class.

如果您只希望一次序列化/反序列化一个类,则最佳做法是对每个序列化类的一个文件进行读/写。

#2


Correct me if I'm wrong but it seems you first save Profile.Store and then you save FishData.Store.

纠正我,如果我错了,但似乎你首先保存Profile.Store然后你保存FishData.Store。

Then on the load you try to retreive the FishData.Store when the Profile.Store is first in the file.

然后在加载时,当Profile.Store在文件中的第一个时,您尝试检索FishData.Store。

(I'm assuming you use the load from script 4 and the save in script 3.)

(我假设您使用脚本4中的加载和脚本3中的保存。)