- Home /
2 problems with serialization of a generic list of a custom class
Hi. I have a serious problem with the serialization of a list of my custom class (LevelInfo) inside of the container class (Information) I want to serialize. I'm using the simple state saving script.
The minor problem is, that the private fields of my class do not get serialized. The main problem is, that when I save Information class and then load it again, the Levelinfo list gets another set of fresh entries. This results in clogging the list with unusable information and I have no clue what went wrong.
Any help is higly appreciated. Thanks in advance.
LevelInfo class:
[System.Serializable]
public class LevelInfo{
[SerializeField]
private float levelTime; //Does not get serialized, despite of [serializefield]
public float parTime; //This does. Set to public for testing purpose
//...
public LevelInfo(){ *** } //Blank Constructor
public LevelInfo(float parTime){ ***} // other constructor
public float ParTime{ get{return parTime;}}
public bool BeatPartime{get{return (levelTime <= parTime);}}
public void BeatLevel(float time){levelTime = Mathf.Min(levelTime, time);}
//...
}
Information class:
[System.Serializable]
public class Information{
[SerializeField]
public List<LevelInfo> levels;
//...
//This is inside of the Information constructor.
//This is the only place I use any LevelInfo constructor.
private void CreateLevelList(){
levels = new List<LevelInfo>();
levels.Add(new LevelInfo(4.0f));
//...
}
}
InformationTransport class:
public class InformationTransport: MonoBehaviour {
public Information info;
void Awake () {
LoadInfo();
}
private void SaveInfo(){
StateStorage.SaveData<Information>(saveKey, info);
}
private void LoadInfo(){
Information newInfo = StateStorage.LoadData<Information>(saveKey);
if(newInfo == null)
info = new Information();
else
info = newInfo;
}
StateStorage class:
public class StateStorage{
public static T LoadData<T>( string key ){
if ( PlayerPrefs.HasKey( key ) ) {
XmlSerializer serializer = new XmlSerializer( typeof( T ) );
Debug.Log(PlayerPrefs.GetString( key ));
StringReader sr = new StringReader( PlayerPrefs.GetString( key ) );
return ( T )serializer.Deserialize( sr );
}else{
return default(T);
}
}
public static void SaveData<T>( string key, T source ){
XmlSerializer serializer = new XmlSerializer( typeof( T ) );
StringWriter sw = new StringWriter();
serializer.Serialize( sw, source );
Debug.Log(sw.ToString());
PlayerPrefs.SetString( key, sw.ToString() );
PlayerPrefs.Save();
}
public static void ClearData( string key ){
PlayerPrefs.DeleteKey( key );
}
}
Answer by Lockstep · Jan 21, 2014 at 12:07 AM
Regarding the [SerialzeField]
problem: I gave my properties a setter in addition to their getter. Resulting in the properies getting serialized. I also reported a bug since the expected behaviour does not match the documentation.
Regarding the clogging list: The problem was the CreateLevelList()
method inside of the constructor of the Information class. The StateStorage class uses this constructor which results in the list beeing fully generated. Then it proceeds to add the saved list items on top of that.
I fixed this by removing the CreateLevelList()
method from the constructor and calling it from outside when there was no saved data to load.
Answer by Uncasid · Jan 20, 2014 at 06:52 PM
Try doing it after inheriting the "ScriptableObject" class
public class LevelInfo : ScriptableObject {
Then create an instance by doing this:
ScriptableObject obj = ScriptableObject.CreateInstance(typeof(LevelInfo));
Then, read this... very good information on serialization in unity http://forum.unity3d.com/threads/155352-Serialization-Best-Practices-Megapost
The reason I post this is because I see no reason why you cannot use the built in serialization methods for the above code.
Thank you for your reply and the informative link.
Unfortunately the ScriptableObject does not work with the stateStorage script since it does not use a constructor. It also does not seem needed, since I'n only refering to each object once.
I tend to believe, that the missing serialization is due a bug. I guess I will simply make my stuff a bit more public.
Neverless the main problem remains. The clogging of the generic list each time I load my class.
Answer by Slobdell · Jan 20, 2014 at 06:32 PM
Doesn't look like you have getters/setters for your private fields. How is it supposed to access that? To serialize an object you need a basic no argument constructor and a getter and setter for each attribute in the object.
Thank you for your reply.
From the documentation and also from the link provided by uncasid, the [SerializeField] modifier would allow me to serialize the private field directly. Unless I'm using this wrong, it seems to be broken.
If you use getters and setters, then their corresponding property gets serialized, not the private field behind them.
I.e. this:
public class someclass{
private int someint;
someclass(){
someint = 5;
}
public int Someint{
get{return someint;}
set{someint = value;}
}
public int RandomInt{
get{return 3* someint;}
set{someint = 2* value -7;}
}
}
would be xml serialized into this (with the Statestorage script):
<Someint>5</Someint>
<RandomInt>15</RandomInt>
and after loading someint would be 23. If you would flip the lines of code for Someint and the RandomInt, the result would still be 5 though. And yes I tested it.
Your answer
Follow this Question
Related Questions
Getting any type in Unity Inspector 1 Answer
List Contains false after playmode 0 Answers
Serializing specialized subclasses of generic classes not working 1 Answer
A node in a childnode? 1 Answer
Error serializing a class to save / load 2 Answers