Cannot Deserialize XML File into a List - c#

My problem is, my program isn't taking data from my XML file and putting it into a list
I can save data just fine but I can't load it back.
Here is my Save function
public void Save_Data_Click(object sender, EventArgs e)
{
XmlSerializer PSR = new XmlSerializer(typeof(Pickup));
XmlSerializer DSR = new XmlSerializer(typeof(Delivery));
TextWriter PickupStream = new StreamWriter(#"Pickup Save Data.xml");
TextWriter DeliveryStream = new StreamWriter(#"Delivery Save Data.xml");
PSR.Serialize(PickupStream, thePickup);
DSR.Serialize(DeliveryStream, theDelivery);
DeliveryStream.Close();
PickupStream.Close();
MessageBox.Show("All data saved!");
}
Here is my xml load code
XmlSerializer SerializerObj = new XmlSerializer(typeof(Pickup));
FileStream ReadFileStream = new FileStream(#"Pickup Save Data.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
Pickup LoadedObj = (Pickup)SerializerObj.Deserialize(ReadFileStream);
ReadFileStream.Close();
Here is my Pickup Class
namespace Vans
{
[Serializable]
public class Pickup
{
public List<Pickups> Pickups = new List<Pickups>();
public void addPickup(Pickups Pic)
{
Pickups.Add(Pic);
}
public List<String> listPickups()
{
List<String> listPickups = new List<string>();
foreach (Pickups pick in Pickups)
{
String pickupString = pick.ToString();
listPickups.Add(pickupString);
}
return listPickups;
}
public Pickups getPickup(int i)
{
int c = 0;
foreach (Pickups Pic in Pickups)
{
if (i == c)
return Pic;
c++;
}
return null;
}
}
}
My Delivery class is the same as the Pickup class.
Edit
<?xml version="1.0" encoding="utf-8"?>
<Pickup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Pickups>
<Pickups>
<pickupfirstname>dsf</pickupfirstname>
<pickuplastname>sdf</pickuplastname>
<pickupdeliveryaddress>sd</pickupdeliveryaddress>
<pickuptime>dsf</pickuptime>
</Pickups>
</Pickups>
</Pickup>
this is some sample XML code

In this example, every field, that should be (de-)serialized, has an annotation [XmlElement], maybe thats the reason...

Make sure your Pickups class is as follows to deserialize it correctly:
public class Pickups
{
public string pickupfirstname;
public string pickuplastname;
public string pickupdeliveryaddress;
public string pickuptime;
}
Note: I have tested your code locally with the inclusion of this class and it is working perfectly fine.

Related

how to Load json for windows build unity

I have tried alot of solution but no one is working in my case, my question is simple but i cant find any answer specially for windows build. I have tried to load json form persistent , streaming and resource all are working good in android but no any solution work for windows build. here is my code please help me.
public GameData gameData;
private void LoadGameData()
{
string path = "ItemsData";
TextAsset targetFile = Resources.Load<TextAsset>(path);
string json = targetFile.text;
gameData = ResourceHelper.DecodeObject<GameData>(json);
// gameData = JsonUtility.FromJson<GameData>(json);
print(gameData.Items.Count);
}
here is my data class
[Serializable]
public class GameData
{
[SerializeField]
public List<Item> Items;
public GameData()
{
Items = new List<Item>();
}
}
public class Item
{
public string Id;
public string[] TexturesArray;
public bool Active;
public Item()
{
}
public Item(string _id, string[] _textureArray , bool _active = true)
{
Id = _id;
TexturesArray = _textureArray;
Active = _active;
}
}
In order to be (de)serialized Item should be [Serializable]
using System;
...
[Serializable]
public class Item
{
...
}
Then you could simply use Unity's built-in JsonUtility.FromJson:
public GameData gameData;
private void LoadGameData()
{
string path = "ItemsData";
TextAsset targetFile = Resources.Load<TextAsset>(path);
string json = targetFile.text;
gameData = JsonUtility.FromJson<GameData>(json);
print(gameData.Items.Count);
}
For loading something from e.g. persistentDataPath I always use something like
var path = Path.Combine(Application.persistentDataPath, "FileName.txt")
using (var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var sr = new StreamReader(fs))
{
var json = sr.ReadToEnd();
...
}
}
For development I actually place my file into StreamingAssets (streamingAssetsPath) while running the code in the Unity Editor.
Then on runtime I read from persistentFilePath. If the file is not there I first copy it from the streamingassetsPath the first time.
Here I wrote more about this approach

Adding new values to dictionary corrupts old save file

I am using a simple method of serializing and deserializing data for my save files which looks like this
//Object that is being stored
[System.Serializable]
public class GameData{
public int units;
public int scanRange;
public int gains;
public int reputation;
public int clicks;
public Dictionary<string,bool> upgradesPurchased;
public Dictionary<string,bool> upgradesOwned;
public Dictionary<string,bool> achievementsEarned;
public GameData(int units_Int,int scan_Range,int gains_Int,int reputation_Int,int clicks_Int,Dictionary<string,bool> upgrades_Purchased,Dictionary<string,bool> upgrades_Owned,Dictionary<string,bool> achievements_Earned){
units = units_Int;
scanRange = scan_Range;
gains = gains_Int;
reputation = reputation_Int;
clicks = clicks_Int;
upgradesPurchased = upgrades_Purchased;
upgradesOwned = upgrades_Owned;
achievementsEarned = achievements_Earned;
}
}
//Method that handles saving the object
public void SaveFile(){
string destination = Application.persistentDataPath + DATA_FILE;
FileStream file;
if (File.Exists (destination)) {
file = File.OpenWrite (destination);
} else {
file = File.Create (destination);
}
GameData data = new GameData (GameManager.Instance.units,GameManager.Instance.scanRange,GameManager.Instance.gains,GameManager.Instance.reputation,GameManager.Instance.clicks,UpgradeManager.Instance.upgradesPurchased,UpgradeManager.Instance.upgradesOwned,AchievementManager.Instance.achievementsEarned);
BinaryFormatter bf = new BinaryFormatter ();
bf.Serialize (file, data);
file.Close ();
NotificationsBar.Instance.ShowNotification ("Game saved success");
}
//Method that loads the object
public void LoadFile(){
string destination = Application.persistentDataPath + DATA_FILE;
FileStream file;
if (File.Exists (destination)) {
file = File.OpenRead (destination);
} else {
UpgradeManager.Instance.FirstLoad ();
return;
}
BinaryFormatter bf = new BinaryFormatter ();
GameData data = (GameData)bf.Deserialize (file);
file.Close ();
GameManager.Instance.units = data.units;
GameManager.Instance.scanRange = data.scanRange;
GameManager.Instance.gains = data.gains;
GameManager.Instance.reputation = data.reputation;
GameManager.Instance.clicks = data.clicks;
UpgradeManager.Instance.upgradesPurchased = data.upgradesPurchased;
UpgradeManager.Instance.upgradesOwned = data.upgradesOwned;
AchievementManager.Instance.achievementsEarned = data.achievementsEarned;
Debug.Log ("Units: " + data.units);
}
Theres a lot of code here but this is so everyone has a clear picture of what the entire system looks like
So the issue with this method is when adding a new value to the dictionary passed to GameData UpgradeManager.Instance.upgradesPurchased I will get an error when searching for data within the dictionary key not present in dictionary
My analysis is that due to the new value being added there is an offset in the dictionary from where the new value is placed and what used to be in that place
What I expected to happen when I first wrote out the code wa the dictionary would just autopopulate the new values and overwrite the old data
For a visual representation of what I mean lets say you have 2 upgrades
Upgrade1,Upgrade2
Now this is saved
Now the code changes and you have 3 upgrades
Upgrade1,Upgrade3,Upgrade2
What I assume would happen is the new value is just added into the save
So I am not exactly sure why this is happening....
Whilst I can't see the exact cause of the issue I would suggest the following:
First, take your save/load logic out of your GameData class and put it into a SaveDataManager class, that way you segregate responsibility.
From there, you can simplify your GameData class down to a struct making serialisation/desrialisation easier.
Then in your main game class whenever you have to load the game you can do something along the lines of:
SaveGameManger sgManager = new SaveGameManager(file);
gameData = sgManager.LoadGame()
This will make your code much easier to maintain and if this doesn't fix your problem it will be a lot easier to find.
Further to this, it will also allow you to build unit tests that verify the integrity of you load and save logic.
I've not had a chance to test it, but your separated and refactored code would look something like this (although it needs some validation checks added and whatnot):
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace temp
{
public class GameLoop
{
private SaveGameManager sgManager;
private GameData data;
private bool isPlaying;
public GameLoop()
{
sgManager = new SaveGameManager("MYSAVELOCATION");
data = sgManager.LoadGame();
isPlaying = true;
}
private void PlayGame()
{
while (isPlaying)
{
//All of your game code
}
}
}
public class SaveGameManager
{
private string saveFile;
private BinaryFormatter formatter;
public SaveGameManager(string file)
{
saveFile = file;
formatter = new BinaryFormatter();
}
public GameData LoadGame()
{
using (StreamReader reader = new StreamReader(saveFile))
{
return (GameData)formatter.Deserialize(reader.BaseStream);
}
}
public void SaveGame(GameData data)
{
using (StreamWriter writer = new StreamWriter(saveFile))
{
formatter.Serialize(writer.BaseStream, data);
}
}
}
[Serializable]
public struct GameData
{
public int units;
public int scanRange;
public int gains;
public int reputation;
public int clicks;
public Dictionary<string, bool> upgradesPurchased;
public Dictionary<string, bool> upgradesOwned;
public Dictionary<string, bool> achievementsEarned;
}
}
And I really would consider switching out your string keys for upgrades in favour of enums... Much less error prone.

Writing/ Reading Multiple Objects into Xml C#

I am new to Xml and have written the code whcih creates an Xml and Reads it back also.
But i want to have some modifications in the Xml structure.
What i don't want is ArrayOfMovie tag, which is coming as the root tab.
But when i am writing multiple objects into the Xml it shows an ArrayOfMovie tag. As i have to maintain the structure of the class, the upper tag as Movie, then its details and then other movie. Also please if you tell the code to modify the xml, tell the procedure to read the newly structured xml too.
Here is the code for the scenario:
// Movies class which contains the list of Movie objects
public class Movies
{
public List<Movie> movieList = new List<Movie>();
}
//Movie class
public class Movie
{
public string Title
{ get; set; }
public int Rating
{ get; set; }
public DateTime ReleaseDate
{ get; set; }
}
private void CreateXml_Click(object sender, EventArgs e)
{
string filePath = path + textBox_XmlFileName.Text+".xml";
Movie firstMov = new Movie();
firstMov.Title = "Shrek";
firstMov.Rating = 2;
firstMov.ReleaseDate = DateTime.Now;
Movie secondMov = new Movie();
secondMov.Title = "Spider Man";
secondMov.Rating = 4;
secondMov.ReleaseDate = DateTime.Now;
Movies moviesObj = new Movies();
moviesObj.movieList.Add(firstMov);
moviesObj.movieList.Add(secondMov);
List<Movie> movList = new List<Movie>() { firstMov,secondMov};
XmlHandler.SerializeToXml(moviesObj.movieList, filePath);
}
// The static class and funcion that creates the xml file
public static void SerializeToXml(List<Movie> movies ,string filePath)
{
XmlSerializer xls= new XmlSerializer(typeof(List<Movie>));
TextWriter tw = new StreamWriter(filePath);
xls.Serialize(tw, movies);
tw.Close();
}
// It Creates the following output
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie>
<Title>Shrek</Title>
<Rating>2</Rating>
<ReleaseDate>2014-05-25T22:55:17.2811063+05:00</ReleaseDate>
</Movie>
<Movie>
<Title>Spider Man</Title>
<Rating>4</Rating>
<ReleaseDate>2014-05-25T22:55:17.2811063+05:00</ReleaseDate>
</Movie>
</ArrayOfMovie>
// The code for reading the file into objects
public static List<Movie> DeserializeFromXml(string filePath)
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Movie>));
TextReader tr = new StreamReader(#filePath);
List<Movie> movie;
movie = (List<Movie>)deserializer.Deserialize(tr);
tr.Close();
return movie;
}
you may use the XmlRootAttribute if you want to name your root
XmlSerializer deserializer = new XmlSerializer(typeof(List<Movie>),
new XmlRootAttribute("YourRoot"));

Empty serialized XML file

i get an empty xmlfile after serializing an object. I'm using Monodevelop and Unity 4. I searched for such a long time mostly in this community, but i only found difficult problems with even more difficult answers :) I think mine is so simple, please help me. (I am new to c#)
The serialized object is this:
[System.Serializable]
public class information {
private string data1;
private string data2;
private string data3;
public void Data1(string text)
{
data1 = text;
}
public string GetData1 ()
{
return data1;
}
public void Data2(string text)
{
data2 = text;
}
public string GetData2 ()
{
return data2;
}
public void Data3(string text)
{
data3 = text;
}
}
the serializing class is this, here might be the problem:
public class SaveXml {
public void SaveData(object obj, string filename)
{
XmlSerializer sr = new XmlSerializer(obj.GetType());
TextWriter writer = new StreamWriter(filename);
sr.Serialize(writer, obj);
writer.Close();
}
public string Load()
{
if(File.Exists("accdata.xml"))
{
XmlSerializer xs = new XmlSerializer(typeof(information));
FileStream read = new FileStream("accdata.xml",FileMode.Open, FileAccess.Read, FileShare.Read);
information info = (information)xs.Deserialize(read);
return info.GetData1();
}
else
{
return "file does not exist";
}
}
And the serializing and the serialized object get called by a menu that has this 2 buttons:
if(GUI.Button(new Rect(10,50,300,100),"Save"))
{
SaveXml saver = new SaveXml();
information infol = new information();
infol.Data1("textone");
infol.Data2("texttwo");
infol.Data3( "textthree");
saver.SaveData(infol, "accdata.xml");
}
if(GUI.Button(new Rect(500,50,300,100),"Load"))
{
SaveXml saver1 = new SaveXml();
text = saver1.Load();
}
so the variable text that is declared in the class menu, should be "textone", after i clicked the Save Button and the LoadButton. The Savebutton creates a file that is empty.
The Deserialization seems to work but of course there is no String in the data1 variable in Information so the variable in the menu called text is empty too. I get no errors and i can work with the object after serialization.
So why doesnt my serialization work? Please help me. I excuse for my bad english and mistakes, i am new to stackoverflow.
Xml serializer serializes public fields/properties not methods. Change your methods to properties. For ex,
public string Data2
{
set { data2 = value; }
get { return data2; }
}
So your information class can be
public class Information
{
public string Data1 { get; set; }
public string Data2 { get; set; }
public string Data3 { get; set; }
}
BTW: you don't need this Serializable attribute. It is only used by BinaryFormatter
I'm not sure but from what i see you don't have any public fields... Take a look here
And also, why don't you just use auto getter/setter ?
According to this MSDN support article, using XmlSerializer the way you have performs only "shallow" serialization - it only serializes public fields/properties. To serialize private data requires "deep" serialization which appears to be a whole other animal.

Custom XML-element name for base class field in serialization

How can I change XML-element name for field inherited from base class while doing serialization?
For example I have next base class:
public class One
{
public int OneField;
}
Serialization code:
static void Main()
{
One test = new One { OneField = 1 };
var serializer = new XmlSerializer(typeof (One));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
I get what I need:
<?xml version="1.0" encoding="utf-8"?>
<One xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
</One>
Now I have created new class inherited from A with additional field and custom XML element name for it:
public class Two : One
{
[XmlElement("SecondField")]
public int TwoField;
}
Serialization code:
static void Main()
{
Two test = new Two { OneField = 1, TwoField = 2 };
var serializer = new XmlSerializer(typeof (Two));
TextWriter writer = new StreamWriter("Output.xml");
serializer.Serialize(writer, test);
writer.Close();
}
As a result I get next output:
<?xml version="1.0" encoding="utf-8"?>
<Two xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OneField>1</OneField>
<SecondField>2</SecondField>
</Two>
The problem is that I want to change OneField in this output to FirstField without touching base class code (because I will use it too and the names must be original). How can I accomplish this?
Thank you.
Try this:
public class Two : One
{
private static XmlAttributeOverrides xmlOverrides;
public static XmlAttributeOverrides XmlOverrides
{
get
{
if (xmlOverrides == null)
{
xmlOverrides = new XmlAttributeOverrides();
var attr = new XmlAttributes();
attr.XmlElements.Add(new XmlElementAttribute("FirstField"));
xmlOverrides.Add(typeof(One), "OneField", attr);
}
return xmlOverrides;
}
}
[XmlElement("SecondField")]
public string TwoField;
}
And your serializer init is a lot easier:
var xmls = new System.Xml.Serialization.XmlSerializer(typeof(Two), Two.XmlOverrides);
Here's a workaround: Override the fields in the subclass and mark the overriden field with whatever name you need. For example,
class One
{
public int OneField { get; set; }
}
class Two : One
{
[XmlElement("FirstField")]
public new int OneField
{
get { return base.OneField; }
set { base.OneField = value; }
}
}

Categories