Deserializing a file into List C# - c#

I'm trying to deserialize a file which contains objects into a list of objects
but for some how the list takes to objects only
here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace Test
{
class Program
{
static void Main(string[] args)
{
user lastuser = new user("First name", "Last name", "Username", "Password");
FileStream ToStream = new FileStream("UsersDB.txt", FileMode.OpenOrCreate);
BinaryFormatter Ser = new BinaryFormatter();
List<user> ToUsers = new List<user>();
try
{
ToUsers = (List<user>)Ser.Deserialize(ToStream); // this is to deserialize everything in the file to the list
ToUsers.Add(lastuser); // here we are adding our object (which is lastuser) to the list
Ser.Serialize(ToStream, ToUsers); // here we are serializing the list back to the file
}
catch (System.Runtime.Serialization.SerializationException)
{//this is to catch the exception if the file was empty and there is nth to deserialize to the list
ToUsers.Add(lastuser);
Ser.Serialize(ToStream, ToUsers);
}
ToStream.Close();
Console.WriteLine("ToUsers objects : " + ToUsers.Count());
// this is to see how many objects does the list have
}
}
}
and this is the class I'm serializing:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization;
namespace Test
{
[Serializable]
class user
{
private string Fname, Lname, Username, Password;
public user()
{ }
public user(string Fname, string Lname, string Username, string Password)
{
this.Fname = Fname;
this.Lname = Lname;
this.Username = Username;
this.Password = Password;
}
public string GetUsername()
{
return Username;
}
}
}
When I run it, I get the count of list is 1.
Run it again I get it 2.
Run it 1000 times and you will get 2.
I know there is something wrong so please help me.

Your code within try is
try
{
ToUsers = (List<user>)Ser.Deserialize(ToStream);
ToUsers.Add(lastuser);
Ser.Serialize(ToStream, ToUsers);
}
what is happening in the above code is, during de-serialization, the stream position pointer is moved to the end of the file. So when you serialize back, the list containing the two objects is appended at the end of the file.
Hence, the new file structure is like
+---------------------+-----------------------------------------------------+
| List (1 user info) | List (2 user's info) |
+---------------------+-----------------------------------------------------+
So, when you de-serialize the next time, you again get the list containing one user's details.
To overwrite the existing data, reset the stream position pointer to the beginning of the file using
ToStream.Seek(0, SeekOrigin.Begin);
Hence, your try block would look like
try
{
ToUsers = (List<user>)Ser.Deserialize(ToStream);
ToStream.Seek(0, SeekOrigin.Begin);
ToUsers.Add(lastuser);
Ser.Serialize(ToStream, ToUsers);
}

The problem is that you're deserializing the first list with one object, adding the item and appending the new list to the file. Next time you open/read the file, you'll read the first list again.
What you need to do is simply rewind the FileStream before serializing the new list to the file;
ToUsers = (List<user>)Ser.Deserialize(ToStream);
ToUsers.Add(lastuser); // here we are adding our object (which is lastuser) to the list
ToStream.Seek(0, SeekOrigin.Begin);
Ser.Serialize(ToStream, ToUsers); // here we are serializing the list back to the file

Related

Issues with Save/Load System in a Text Based Adventure game made with ScriptableObjects in Unity

I am almost done with my Text Based Adventure Game and am trying to setup a Save/Load system that works. I can't seem to accomplish this.
I have viewed multiple tutorials and I feel that Json may be what I need. Unfortunately I can't seem to get Json code correct for the 'Load' part. I am also concerned that the amount of data I am trying to save is too much for a simple Serialization process OR I am over-complicating this and there is a simpler solution.
This is the data I am tryin to Save/Load. As you can see I have Dictionaries and Lists, as well as a 'Room' ScriptableObject that I am trying to save, namely, the currentRoom the Player is in when the game is saved. The currentRoom is tracked in the GameController.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class SaveData
{
public Room saveRoomNavigation;
public List<string> saveNounsInInventory = new List<string>();
public List<InteractableObject> saveUseableItemList = new
List<InteractableObject>();
public Dictionary<string, string> saveExamineDictionary = new
Dictionary<string, string>();
public Dictionary<string, string> saveTakeDictionary = new
Dictionary<string, string>();
public List<string> saveNounsInRoom = new List<string>();
public Dictionary<string, ActionResponse> saveUseDictionary = new
Dictionary<string, ActionResponse>();
public SaveData (GameController controller, InteractableItems
interactableItems)
{
saveRoomNavigation = controller.roomNavigation.currentRoom;
saveNounsInInventory = interactableItems.nounsInInventory;
saveUseableItemList = interactableItems.useableItemList;
saveExamineDictionary = interactableItems.examineDictionary;
saveTakeDictionary = interactableItems.takeDictionary;
saveNounsInRoom = interactableItems.nounsInRoom;
saveUseDictionary = interactableItems.useDictionary;
}
}
Then this is my Save System code where I am trying to implement the Serialization with Json and binary.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class SaveSystem
{
public static void SaveGame (GameController controller,
InteractableItems interactableItems)
{
BinaryFormatter formatter = new BinaryFormatter();
string path = Application.persistentDataPath + "/savegame.rta";
FileStream stream = new FileStream(path, FileMode.Create);
SaveData data = new SaveData(controller, interactableItems);
var json = JsonUtility.ToJson(data);
formatter.Serialize(stream, json);
stream.Close();
}
public static SaveData LoadGame()
{
string path = Application.persistentDataPath + "/savegame.rta";
if (File.Exists(path))
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Open);
SaveData data =
JsonUtility.FromJsonOverwrite((string)formatter.Deserialize(stream,
????));
//SaveData data = formatter.Deserialize(stream) as
SaveData;
stream.Close();
return data;
}
else
{
Debug.LogError("Save file not found in " + path);
return null;
}
}
}
Where I put the '????' above is where I can't seem to get it to accept the Object I am trying to overwrite, which I thought was 'SaveData' since that is where I pull the information to put into the game when the Player clicks the 'Load' button. It tell me "'SaveData' is a type, which is not valid in the given context"
Here is the code I call when the 'Save' and 'Load' buttons are used, it is within my GameController Class where most everything is tracked during Game Play.
public void SaveGame()
{
SaveSystem.SaveGame(this, interactableItems);
}
public void LoadGame()
{
SaveData data = SaveSystem.LoadGame();
roomNavigation.currentRoom = data.saveRoomNavigation;
interactableItems.nounsInInventory = data.saveNounsInInventory;
interactableItems.useDictionary = data.saveUseDictionary;
interactableItems.takeDictionary = data.saveTakeDictionary;
interactableItems.examineDictionary =
data.saveExamineDictionary;
interactableItems.useableItemList = data.saveUseableItemList;
interactableItems.nounsInRoom = data.saveNounsInRoom;
DisplayRoomText();
DisplayLoggedText();
}
When used in game, I get "SerializationException" errors, so that is when I attempted to try Json. I read where it can serialize a lot more than the basic Unity Serializer. The errors went away and it seems to save OK with Json, but now I am unable to get the Load syntax correct.
I am a novice so maybe I am making this harder then it has to be... I also looked into Userprefs, but I don't think I can convert the Scriptable Object, Room, into a type String.
Thanks in advance!
[UPDATE] Here is what I am able to have it save in .txt format. This proves that it is saving, just not sure "instanceIDs" will give me the Load that I need.
ˇˇˇˇö{"saveRoomNavigation":
{"instanceID":11496},"saveNounsInInventory": ["sword"],"saveUseableItemList":[{"instanceID":11212}, {"instanceID":11588},{"instanceID":10710},{"instanceID":11578}, {"instanceID":10718},{"instanceID":11388}, {"instanceID":11276}],"saveNounsInRoom":["rocks","hole"]}
Take a look at the documentation for Unity's built-in JsonUtility:
https://docs.unity3d.com/Manual/JSONSerialization.html
It says types such as "Dictionaries" are not supported. Seeing as though you have several Dictionaries in your SaveData example, might I suggest using a more robust JSON library such as JSON.NET, which is free on the asset store:
https://assetstore.unity.com/packages/tools/input-management/json-net-for-unity-11347

Binary Serialization of an Observable Collection -- Saving, Loading

I am having a problems creating binary serialization for my observable collection of Contacts Objects. The Person Object contains the firstname,lastname, and age properties.
The collection is called NewAccountList.
I want to be able to persist my collection data by saving it to a file. This action currently occurs when the save button is clicked, but it does xml serialization instead of binary serialization.
private void MenuItem_Click_2(object sender, RoutedEventArgs e)
{
// Create OpenFileDialog
Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
XmlSerializer xs = new XmlSerializer(typeof(ObservableCollection));
sfd.Filter = "XML files (.xml)|.xml";
// Display OpenFileDialog by calling ShowDialog method
Nullable<bool> result = sfd.ShowDialog();
try
{
using (StreamWriter wr = new StreamWriter("SavedAccounts.xml"))
{
xs.Serialize(wr, NewAccountList);
}
}
catch
{}
}
Would anybody be able to give me a good example of how to save a collection of objects?
full code example
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using (FileStream fs = File.Create("data.hffgfh"))
{
bf.Serialize(fs, 123456);
}
}
}
}
output:
Try changing .xml to .bin. That should fix your problem

Why doesn't FileStream as an argument to Streamwriter write to text file?

In the code included below, I am able to write the contents of the string 'fullname' to a text file in the specified directory when using the following statement:
System.IO.File.WriteAllText(path, fullname);
However, if I write the string path to a FileStream object (withe arguments specified), and then pass that FileStream object as an argument to the StreamWriter object, the file is created, but no contents are written.
First attempt: Comment out System.IO.File.WriteAllText(path, fullname); and use the three lines above it. This creates the file but no contents are written into the file.
Second attempt: Un-comment the System.IO.File.WriteAllText(path, fullname); statement and comment the three lines above it. This executes as desired.
Here is the full block of code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace FileInputOutput
{
class Program
{
static void Main(string[] args)
{
// Use the Split() method of the String Class
string fullname = " Robert Gordon Orr ";
fullname = fullname.Trim();
string[] splitNameArray = fullname.Split(' ');
Console.WriteLine("First Name is: {0}", splitNameArray[0]);
Console.WriteLine("Middle Name is: {0}", splitNameArray[1]);
Console.WriteLine("Last Name is: {0}", splitNameArray[2]);
Console.WriteLine("Full name is: {0}", fullname);
string path = #"C:\Programming\C#\C# Practice Folder\Console Applications\FileInputOutput\textfile.txt";
FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
StreamWriter toFile = new StreamWriter(fs);
toFile.Write(fullname);
//System.IO.File.WriteAllText(path, fullname);`enter code here`
Console.ReadLine();
}
}
}
As others have said: streams must be flushed in .NET in order for them to write to disk. This can be done manually, however I would simply change your code to have using statements on your streams:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace FileInputOutput
{
class Program
{
static void Main(string[] args)
{
// Use the Split() method of the String Class
string fullname = " Robert Gordon Orr ";
fullname = fullname.Trim();
string[] splitNameArray = fullname.Split(' ');
Console.WriteLine("First Name is: {0}", splitNameArray[0]);
Console.WriteLine("Middle Name is: {0}", splitNameArray[1]);
Console.WriteLine("Last Name is: {0}", splitNameArray[2]);
Console.WriteLine("Full name is: {0}", fullname);
string path = #"C:\textfile.txt";
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite))
{
using (StreamWriter toFile = new StreamWriter(fs))
{
toFile.Write(fullname);
}
}
//System.IO.File.WriteAllText(path, fullname);`enter code here`
Console.ReadLine();
}
}
}
Calling Dispose() on a stream (as using implicitly does), causes the stream to be flushed and closed at the end of the using block.
I think you are just forgetting to flush your file stream:
fs.Flush();
This is needed because according to msdn, this is what makes the FileStream to actually write the buffer to the file.
Flush: Clears buffers for this stream and causes any buffered data to be written to the file. (Overrides Stream.Flush().)
Regards.
From MSDN on StreamWriter
You must call Close to ensure that all data is correctly written out to the underlying stream.
So the problem here is mainly that, since you don't actually close the StreamWriter, the data gets backed up but doesn't push to the file, even though the FileStream immediately created the file in its constructor. Never ever forget to close your stream, as failing to do so could lead to major problems down the line.

files and binary formatter

I want to ask how to create and write a text file using binary formatter I have written the below code but I have an exception.
the exception is :
file can not been access because it is has been used by another process.
and my code creates two files one is with extension ".txt" and the other without any extension.
what should I do?
Is there another way to create a text file ?
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
namespace ConsoleApplication9
{
[Serializable]
class contact
{
string name;
string address;
string phonenumber;
string emailaddress;
public override string ToString()
{
return name + " " + address + " " + phonenumber + " " + emailaddress;
}
public void AddContent(string cname, string caddress, string cphone, string cemail)
{
name = cname;
address = caddress;
phonenumber = cphone;
emailaddress = cemail;
FileStream file = new FileStream("contact.txt", FileMode.OpenOrCreate, FileAccess.Write);
BinaryFormatter bin = new BinaryFormatter();
contact person = new contact();
person.name = cname;
person.address = caddress;
person.phonenumber = cphone;
person.emailaddress = cemail;
bin.Serialize(file, person);
file.Close();
Console.WriteLine(" added sucefully");
}//end of fun add
}
Your exception has nothing to do with the BinaryFormatter but instead with the fact that you are not disposing of the FileStream properly. Always wrap streams in a using block:
using(FileStream file = new FileStream("contact.txt", FileMode.OpenOrCreate, FileAccess.Write))
{
//code here
}
This guarantees that the stream underneath is closed and the unmanaged resources are freed. In your case in particular, it seems that the OS still has a lock to the file you are attempting to create and the second time you run your code throws the exception about your file being used by another process
file can not been access because it is has been used by another process.
Is it possible that you may have in advertantly opened this file and its still open in a text window?

Edit .Bat File In C#

Well, first of all my code :
protected void Button2_Click1(object sender, EventArgs e)
{
string batname = edit.SelectedValue;
StreamWriter sw = new StreamWriter("D:\\MPSite-Serv\\bats\\" + batname);
string theedit = batedit.Text;
sw.WriteLine(theedit);
sw.Flush();
}
When I click on button2 and try to write all of the text into the bat file, I get this result the bat file contains:
System.Web.UI.WebControls.TextBox
Why is that?
I am using all of the following statements, if it helps:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Diagnostics;
You are getting the type name of the textbox, which leads me to believe that your code rather looks something like:
sw.WriteLine(batedit);
I.e. you are sending the textbox object itself to be written to the file instead of the contents of its Text property. This will implicitly call the ToString method, which by default returns the type name of the object.
Also, you are not closing the StreamWriter properly, which might cause problems when you want to use it. You should call the Close method, and you don't have to call Flush before closing the StreamWriter. Alternatively you can put the StreamWriter in a using block, which would dispose it automatically, which will close it.
http://msdn.microsoft.com/en-us/library/system.io.streamwriter.aspx#Y2863
The problem is simple: sw.writeline expects a string. You are sending it a text object which gets turned into the aformentioned .ToString(); which means your line gets translated, loosely, into
sw.WriteLine(theedit.ToString());
if you look at the documentation for WebControls.TextBox... scroll down the list to the Method ToString()
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.textbox.aspx
What you need to do... is give it one string at a time (although I assume you could just give it the raw string from the textbox):
using System.IO;
namespace String_Writer
{
class Program
{
static void Main(string[] args)
{
string batname = "test.txt";
string theedit = "Testing one two three four\n\nfive six seven eight.";
using(StreamWriter sw = File.CreateText("C:\\Users\\Kriis\\Desktop\\" + batname))
{
using (StringReader reader = new StringReader(theedit))
{
string line = string.Empty;
do
{
line = reader.ReadLine();
if (line != null)
{
sw.WriteLine(line);
}
} while (line != null);
}
}
}
}
}

Categories

Resources