How is it possible to extend the following code to be executed in a seperate thread?
The methods calling save/load:
private void Save()
{
ClassDiagram diagram = new ClassDiagram();
diagram.ClassDatas = ClassDatas.ToList();
diagram.Connectors = Connectors.ToList();
String filePathAndName = GetSaveFilePathAndName();
saveLoadSerialization.Save(filePathAndName, diagram);
}
private void Open()
{
String filePathAndName = GetOpenFilePathAndName();
ClassDiagram diagram = saveLoadSerialization.Load<ClassDiagram>(filePathAndName);
ClassDatas = new ObservableCollection<ClassData>(diagram.ClassDatas);
Connectors = new ObservableCollection<Connector>(diagram.Connectors);
Connectors.ToList().ForEach(c => { c.From = ClassDatas.Single(cd => cd.Number == c.FromID); c.To = ClassDatas.Single(cd => cd.Number == c.ToID); });
RaisePropertyChanged("ClassDatas");
RaisePropertyChanged("Connectors");
}
The Save/Load XMLSerializer code looks like this - is the best approach an asynchronous thread?
public void Save<T>(String filePathAndName, T data)
{
try
{
using (FileStream file = File.Create(filePathAndName))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
serializer.Serialize(file, data);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public T Load<T>(string filePathAndName)
{
try
{
using (FileStream file = File.OpenRead(filePathAndName))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(file);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return default(T);
}
Related
I have a class called Member that I want to save and load in a XML file.
Here is my methods to load/save
public T LoadGenericDataType<T>(string filename)
{
XmlSerializer xms = new XmlSerializer(typeof(T));
FileStream fileStream = new FileStream(this._rootFolder + filename, FileMode.Open, FileAccess.ReadWrite);
try
{
Object v = xms.Deserialize(fileStream);
fileStream.Close();
return (T) v;
}
catch (Exception e)
{
fileStream.Close();
throw e;
}
}
public bool SaveGenericDataType<T>(T data, string filename)
{
XmlSerializer xms = new XmlSerializer(typeof(T));
FileStream fileStream = new FileStream(this._rootFolder + filename, FileMode.Create, FileAccess.Write);
try
{
xms.Serialize(fileStream, data);
fileStream.Close();
return true;
}
catch (Exception e)
{
fileStream.Close();
return false;
}
}
In my member class I save a property called Boats, and I don't want to send the original List to any other class so I do a copy of it.
public List<Boat> Boats
{
get => _boats; // this will break encapsulation, but will make it easier to serialize( bad design my me :/ )
set => _boats = value;
}
public List<Boat> Boats1
{
get
{
List<Boat> copyOfBoats = new List<Boat>(); // this will cause LOADING to fail not saving.
foreach (Boat boat in _boats)
{
copyOfBoats.Add(boat);
}
return copyOfBoats;
}
set => _boats = value;
}
What am I missing? If I try to load an xml with the "Boats1" Property it will fail.
Okay I managed to fixed the problem by this.
public List<Boat> Boats
{
get
{
if (_boats.Count == 0)
return _boats;
List<Boat> copyOfBoats = new List<Boat>(); .
foreach (Boat boat in _boats)
{
copyOfBoats.Add(boat);
}
return copyOfBoats;
}
set => _boats = value;
}
But it will still break encapsulation if the member don't have any boats.
Maybe try
public List<Boat> Boats1
{
get
{
List<Boat> copyOfBoats = new List<Boat>();
if (_boats.Count > 0)
{
foreach (Boat boat in _boats)
{
copyOfBoats.Add(boat);
}
}
return copyOfBoats;
}
set => _boats = value;
}
That should keep it from returning the orginal list of boats.
EDIT: sorry the code is working as intended. I just failed to test properly. Sorry for your inconvenience
I found some code here on SO to store and load objects (Code in the end). Storing the file is correctly working, but getting the file into object back again is not working when you have a list of objects:
Executing
Block b = loadFile<Block>("file");
Console.WriteLine(b.allCoins.Count); //is 0
results in an empty List. Checking the xml file they are all correctly stored, which means that the loading is somehow not working. How can you correctly load the object?
Here is the block class:
[Serializable]
public class Block {
public struct Coin {
public string owner;
public string name;
public Coin(string n, string o) {
owner = o;
name = n;
}
};
public int name;
public List<string> hashOfParticles;
public int numberOfTransactions;
public List<Coin> allCoins;
}
}
Here is how i load the file into objects:
public static T loadFile<T>(string fileName) {
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try {
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString)) {
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read)) {
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
} catch (Exception ex) {
//Log exception here
}
return objectOut;
}
Here is the code which stores the file:
public static void storeFile<T>(T serializableObject, string fileName) {
if (serializableObject == null) { return; }
try {
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream()) {
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileName);
stream.Close();
Form1.instance.addToLog("Storing \"" + fileName + "\" succesful");
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
Form1.instance.addToLog("Storing \"" + fileName + "\" NOT succesful");
}
}
I am trying to read from a xml file but it is not working eventhough I have been weating over it for two days, so any help would be greatly appreciated!
In Cookbook class:
public List<Recipe> readAll()
{
List<Recipe> newListRecipies = new List<Recipe>();
Recipe readRecipie = new Recipe();
TextReader reader = null;
try
{
var serializer = new XmlSerializer(typeof(Recipe));
reader = new StreamReader(path);
newListRecipies = BinarySerialization.ReadFromBinaryFile<List<Recipe>>(path);
reader.Close();
return newListRecipies;
}
catch (Exception e)
{
string error = $"An exception occured: " + e;
Log theLog = new Log();
theLog.LogMessage(error);
return newListRecipies;
}
}
In Recipe class:
public Recipe readOne(string name)
{
CookBook newCB = new CookBook();
List<Recipe> allRecipies = newCB.readAll();
foreach(Recipe oneRecipe in allRecipies)
{
if(oneRecipe.recipeName == name)
{
return oneRecipe;
}
}return newCB.defaultRecipie;
}
I am getting the default recipe as the result everytime. I can see the the recipies are saved correctly everytime but here the code anyways:
In Recipie class:
public void SaveRecipe(Recipe myRecepie)
{
CookBook theCookBook = new CookBook();
theCookBook.Save(myRecepie);
addFoodItem(myRecepie.recipeIngridients);
}
In CookBook class:
public void Save(Recipe newRecipie)
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof(Recipe));
writer = new StreamWriter(path, append: true);
serializer.Serialize(writer, newRecipie);
}
finally
{
if (writer != null)
writer.Close();
}
}
And the xml file (generated by the save function in the CookBook class)
<?xml version="1.0" encoding="utf-8"?>
<Recipe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<recipeName>toast</recipeName>
<recipeType>snack</recipeType>
<recipeIngridients>
<string>bread</string>
<string>butter</string>
</recipeIngridients>
</Recipe><?xml version="1.0" encoding="utf-8"?>
<Recipe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<recipeName>G&T</recipeName>
<recipeType>drink</recipeType>
<recipeIngridients>
<string>tonic</string>
<string>gin</string>
</recipeIngridients>
</Recipe><?xml version="1.0" encoding="utf-8"?>
<Recipe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<recipeName>cake</recipeName>
<recipeType>snack</recipeType>
<recipeIngridients>
<string>butter</string>
<string>sugar</string>
</recipeIngridients>
</Recipe>
I believe the way you are deserializing the xml is incorrect with BinarySerialization.ReadFromBinaryFile....
Assuming your xml is correct I would do something like this.
// read file
List<Recipe> recipes;
using (var reader = new StreamReader("recipe.xml"))
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Recipe>),
new XmlRootAttribute("Recipe"));
recipes = (List<Recipe>)deserializer.Deserialize(reader);
}
These are the changes I made. It's best to load the previous recipes, add to the list, then re-write the XML from scratch.
public class Recipe
{
public string recipeName;
public string recipeType;
public List<string> recipeIngridients = new List<string>();
public Recipe readOne(string name)
{
CookBook newCB = new CookBook();
List<Recipe> allRecipies = newCB.readAll();
foreach(Recipe oneRecipe in allRecipies)
{
if(oneRecipe.recipeName == name)
{
return oneRecipe;
}
}
return newCB.defaultRecipe;
}
}
public class RecipeList
{
public List<Recipe> Recipes = new List<Recipe>();
}
public class CookBook
{
public Recipe defaultRecipe;
public string path;
public void Save(Recipe newRecipe)
{
TextWriter writer = null;
RecipeList recipeList = null;
try
{
// See if recipes exists
var serializer = new XmlSerializer(typeof(RecipeList));
if (File.Exists(path)) // Load the recipe list if it exists
{
using (var fileStream = File.OpenRead(path))
{
recipeList = (RecipeList)serializer.Deserialize(fileStream);
}
}
else
{
recipeList = new RecipeList();
}
// Add recipe to the list
recipeList.Recipes.Add(newRecipe);
writer = new StreamWriter(path, append: false);
serializer.Serialize(writer, recipeList);
}
finally
{
if (writer != null)
writer.Close();
}
}
public List<Recipe> readAll()
{
RecipeList temp = null;
var serializer = new XmlSerializer(typeof(RecipeList));
try
{
using (var fileStream = File.OpenRead(path))
{
temp = (RecipeList)serializer.Deserialize(fileStream);
}
return temp.Recipes;
}
catch (Exception e)
{
string error = #"An exception occured: " + e;
//Log theLog = new Log();
//theLog.LogMessage(error);
return new List<Recipe>();
}
}
}
I am getting the error "Exception thrown: 'System.NotSupportedException' in mscorlib.ni.dll Memory stream is not expandable" when trying to serialize and save an instance of a custom class object.
Here are my saving/loading methods:
public void SerializeObject<T>(T serializableObject, string fileName)
{
if (serializableObject == null) { return; }
try
{
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream())
{
// convert string to stream
byte[] byteArray = Encoding.UTF8.GetBytes(fileName);
MemoryStream fileNameStream = new MemoryStream(byteArray);
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileNameStream);
stream.Dispose();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
Debug.WriteLine(TAG + serializableObject.ToString() + " saved");
}
public T DeSerializeObject<T>(string fileName)
{
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try
{
string attributeXml = string.Empty;
// convert string to stream
byte[] byteArray = Encoding.UTF8.GetBytes(fileName);
MemoryStream stream = new MemoryStream(byteArray);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(stream);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString))
{
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = XmlReader.Create(read))
{
objectOut = (T)serializer.Deserialize(reader);
reader.Dispose();
}
read.Dispose();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
if (objectOut != null) Debug.WriteLine(TAG + objectOut.ToString() + " loaded");
return objectOut;
}
And here is the object class that I'm trying to save:
public class EntryDataType
{
readonly string TAG = "EntryDataType: ";
private static int idCounter = -1;
public int id;
private EntryDataType parentEdt;
public EntryDataType parentEdtProperty
{
get { return parentEdt; }
set { parentEdt = value; }
}
// row control is not serializable, so ignore it when saving
[XmlIgnore]
public RowControl linkedRowControl;
public int indent = -1;
public int index = -1;
public int linearIndex = -1;
private bool completed = false;
public bool completedProperty {
get { return completed; }
set
{
// set hidden state and set all children's hidden state, i.e. they will do the same
completed = value;
foreach (var item in childList)
{
item.linkedRowControl.SetCompleted(value);
}
}
}
public ChildList<EntryDataType> childList;
public bool bulletButtonChecked;
public string textboxText;
public EntryDataType()
{
// assign unique id to each entry
id = idCounter;
idCounter++;
//Debug.WriteLine(TAG + "new entry " + id + " created");
childList = new ChildList<EntryDataType>();
childList.parentEdtOfChildListProperty = this;
}
}
I've already rewritten the class to eliminate it's constructor's parameters, and to ignore the unserializeable RowControl member. I am just learning .NET and c# so don't fully know what I'm doing yet; any help is greatly appreciated. Thanks :)
OK, I think I see what you are trying to do - serialize and deserialize an object to/from a file. Your way is a bit complicated, it could be simplified, for example like this:
public static void SerializeObject<T>(T serializableObject, string fileName)
{
if (serializableObject == null) { return; }
try
{
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (Stream stream = File.Open(fileName, FileMode.Create))
{
serializer.Serialize(stream, serializableObject);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
public static T DeSerializeObject<T>(string fileName)
{
if (string.IsNullOrEmpty(fileName)) { return default(T); }
try
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (Stream stream = File.Open(fileName, FileMode.Open))
{
return (T)serializer.Deserialize(stream);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return default(T);
}
There is no need to read/write to a MemoryStream first. You can serialize and deserialize straight from the file.
Also, when using using, there is no need to dispose the object (like your stream.Dispose(); line) - that's what the dispose is for (with the added bonus that if there's an exception, the object will be disposed as well).
I haven't tried this with your class but it should work fine. Give it a try and see if it works.
byte[] byteArray = Encoding.UTF8.GetBytes(fileName);
MemoryStream fileNameStream = new MemoryStream();
...
xmlDocument.Save(fileNameStream);
I want to visualize an object with a custom Debugger Visualizer in VS2010. But the object of that class does not have Serializable property.
Since the Source code is written and maintained for long, I don't want to change the class to Serializable for Debugger Visualizer purpose only.
Can someone tell me how to achieve this?
I am here attaching the code with which i achieved what i asked in the question using Json.net from Newtonsoft.
namespace MyCustomVisualizer
{
public class MyObjectSource : VisualizerObjectSource
{
public override void GetData(object target, Stream outgoingData)
{
try
{
byte[] byteArray = JsonHelper.Serialize(target);
outgoingData.Write(byteArray, 0, byteArray.Length);
}
catch (Exception exp)
{
MessageBox.Show(exp.Message, "VisualizerObjectSource Error");
}
}
}
public class MyVisualizer : DialogDebuggerVisualizer
{
override protected void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
{
try
{
string _str = null;
Stream _stream = objectProvider.GetData();
if (_stream != null && _stream.Length > 0)
{
object _obj = null;
_obj = JsonHelper.Deserialize(_stream); // Here i get the object i want
// ^^^
// Now add ur code to visualize the object in your way.
/* This is only to verify the object data before visualizing.
if (_obj != null)
{
_str = JsonHelper.ObjectToString(_obj);
MessageBox.Show(_str, "Show");
}
*/
}
// Show the grid with the list
windowService.ShowDialog();
}
catch (Exception exp) { MessageBox.Show(exp.Message, "Visualizer Error"); }
finally
{
}
}
}
#region JsonHelper Class
public static class JsonHelper
{
public static byte[] Serialize(object _Object)
{
MemoryStream _MemoryStream = new MemoryStream();
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
serializer.TypeNameHandling = TypeNameHandling.Auto;
try
{
using (StreamWriter sw = new StreamWriter(_MemoryStream))
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, _Object);
}
}
catch (Exception exp)
{
MessageBox.Show(exp.Message, "Serialize Error");
}
return _MemoryStream.ToArray();
}
public static object Deserialize(Stream _ByteArray)
{
Object _object = new Object();
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
serializer.TypeNameHandling = TypeNameHandling.Auto;
try
{
StreamReader sw = new StreamReader(_ByteArray);
JsonReader reader = new JsonTextReader(sw);
_object = serializer.Deserialize(reader);
}
catch (Exception exp)
{
MessageBox.Show(exp.Message, "Deserialize Error");
}
return _object;
}
public static string ObjectToString(object _object)
{
string _str = string.Empty;
JsonSerializerSettings _jsonSerializeSettings = new JsonSerializerSettings();
_jsonSerializeSettings.NullValueHandling = NullValueHandling.Ignore;
_jsonSerializeSettings.TypeNameHandling = TypeNameHandling.Auto;
_str = JsonConvert.SerializeObject(_object, Newtonsoft.Json.Formatting.Indented, _jsonSerializeSettings);
return _str;
}
}
#endregion
}
There's no way to do with a BinaryFormatter since it expects the class to be marked as serializable. However you can use different serializer like:
JSON.NET
Protobuf-net
I had the same issue, since I didn't want to change my entire object model I wrote a helper library SInject that inject the serialization attribute on a given assembly for all types excluding abstracts types, interfaces and non public types. if u want to stick with the BinaryFormatter try that library, It can be done with a MSBuild task after the build.
if u'r interested here there's a MSDN guide on how to write a MSBuild task