I am trying to make a winform application. The app has 2 textboxes (firstName, lastName), a numericUpDown, and a checkbox. The app is able to read from a text file, with comma separated rows (Daniel,Brown,26,true). The app put this info in a listbox. Then you can add a new user. When you are finished adding users you press save and the new info from lisbox will be saved in that text file. I've created the read file script and add user succesfully. However I can't create the save user button so that it'll save: Daniel,Brown,26,true. I was able to save as: Daniel,Brown,26,happy.
Here is the Person Class:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Age { get; set; }
public bool IsHappy { get; set; }
public override string ToString()
{
var statusText = IsHappy ? "happy" : "not happy";
return $"{FirstName} {LastName} is {Age} and is {statusText}";
}
}
Here is the form.cs with it's script:
public partial class ChallengeForm : Form
{
private BindingList<Person> _persons = new BindingList<Person>();
private PersonsService _personsService;
public ChallengeForm()
{
_personsService = new PersonsService();
InitializeComponent();
WireUpDropDown();
}
private void WireUpDropDown()
{
_persons = new BindingList<Person>(_personsService.GetPersons(#"C:\Users\user\Desktop\Document.TXT"));
usersListBox.DataSource = _persons;
}
private void addUserButton_Click(object sender, EventArgs e)
{
var person = new Person { FirstName = firstNameText.Text, LastName = lastNameText.Text, Age = agePicker.Text, IsHappy = isHappyCheckbox.Checked };
_persons.Add(person);
}
private void saveListButton_Click(object sender, EventArgs e)
{
}
}
My question is how can I convert the status back to bool. And write the listbox to the text file as csv. I would be very thankfull if you could use SoC.
Here is what I've tried:
const string sPath = (#"C:\Users\user\Desktop\Document.TXT");
System.IO.StreamWriter SaveFile = new System.IO.StreamWriter(sPath);
SaveFile.Write(myperson);
foreach (var item in usersListBox.Items)
{
List<string> unwantedWords = new List<string> { "is", "and" };
var linesSplitted = item.ToString().Split(' ').ToList();
var wordsWithoutUnwantedWords = linesSplitted.Where(i => !unwantedWords.Contains(i)).ToList();
for (int i = 0; i<wordsWithoutUnwantedWords.Count; i++)
{
var isLastWord = i == wordsWithoutUnwantedWords.Count - 1;
SaveFile.Write(wordsWithoutUnwantedWords[i]);
if (!isLastWord)
{
SaveFile.Write(",");
}
Look into XML Serialization. You can just pass in your filepath and the object and the rest will be done for you by the Serialisation classes. Code below:
This is taken from DeadlyDog's awesome answer: https://stackoverflow.com/a/22417240/1623971
/// <summary>
/// Writes the given object instance to an XML file.
/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [XmlIgnore] attribute.</para>
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToXmlFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof(T));
writer = new StreamWriter(filePath, append);
serializer.Serialize(writer, objectToWrite);
}
finally
{
if (writer != null)
writer.Close();
}
}
/// <summary>
/// Reads an object instance from an XML file.
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object to read from the file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the XML file.</returns>
public static T ReadFromXmlFile<T>(string filePath) where T : new()
{
TextReader reader = null;
try
{
var serializer = new XmlSerializer(typeof(T));
reader = new StreamReader(filePath);
return (T)serializer.Deserialize(reader);
}
finally
{
if (reader != null)
reader.Close();
}
}
Sample use case:
WriteToXmlFile<Person>("C:\someClass.txt", objectToSerialize);
// Read the file contents back into a variable.
Person deserializedObject = ReadFromXmlFile<Person>("C:\someClass.txt");
Related
private void SMSend_Clicked(object sender, EventArgs e)
{
var text = myeditor.Text;
}
I am trying to create a sms sender project with Xamarin. And text will be send through api.So i need to convert the text to xml. I am holding the text in the "text" field. I need to convert this text into the xml sytle like converting to JSON. I found something like
var xml = new XmlSerializer(typeof());
but I don't know if it is right to use.And I dont know how to implement these methods. Thank you for helping me!
You can just create a method to convert your text to xml as string.
Something like this will work:
var serializer = new XmlSerializer(typeof(T));
var sw = new Utf8StringWriter();
serializer.Serialize(sw, typeOfObjectToConvert);
ret = sw.ToString();
T is here the type of Object that you want to serliaze to xml.
I create a method for you that you can try :
/// <summary>
/// Converts an object to xml string
/// </summary>
/// <typeparam name="T">the type of the object</typeparam>
/// <param name="typeOfObjectToConvert">the object that needs to be serialized</param>
/// <returns>xml string</returns>
public static string ConvertObjectToPlainXmlString<T>(
T typeOfObjectToConvert)
{
var serializer = new XmlSerializer(typeof(T));
var sw = new Utf8StringWriter();
serializer.Serialize(sw, typeOfObjectToConvert);
return sw.ToString();
}
As an example :
public class MyConfig{
public string TransferUrl {get; set;}
public string TransferData {get; set;}
//.. all properties
}
Than just call method like this:
var xmlString = ConvertObjectToPlainXmlString(MyConfig);
This references my last question which appears to have been abandoned. I am experiencing an odd "bug" if you will with C# and MS VS 2015. To reproduce the error, follow the steps:
Open console app project and copy paste code below.
Set a break point here:
First run code past break point, it works! :D
Then run code again but this time STOP at the break point and DRAG the executing statement cursor INTO the if statement from here:
to here:
Hit Continue and an NRE exception is thrown. Why does this happen? Is it just me? What is the technical explination for this?
CODE:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace testapp
{
class Program
{
static void Main(string[] args)
{
FILECollection randomCollection = new FILECollection();
// Fill with junk test data:
for(int i = 0; i<10; i++)
{
FILE junkfile = new FILE() { fileName = i.ToString(), folderName = i.ToString(), fileHashDigest = new byte[1] };
randomCollection.Add(junkfile);
}
if (true)
{
Console.WriteLine("testing this weird exception issue...");
FILE test;
test = new FILE();
test.fileName = "3";
test.folderName = "3";
test.fileHashDigest = new byte[1];
FILE exists = randomCollection.Where(f => f.fileName == test.fileName &&
f.fileHashDigest.SequenceEqual(test.fileHashDigest)).First();
}
}
}
public class FILE
{
public FILE() { _fileName = "";}
private string _fileName;
public string fileName
{
get
{
if (false)
return this._fileName.ToUpper();
else
return this._fileName;
}
set
{
if (false)
this._fileName = value.ToUpper();
else
this._fileName = value;
}
}
public string folderName { get; set; }
public byte[] fileHashDigest { get; set; }
}
public class FILECollection : IEnumerable<FILE>, ICollection<FILE>
{
private HashSet<FILE> svgHash;
private static List<FILE> PreallocationList;
public string FileName = "N/A";
/// <summary>
/// Default Constructor, will not
/// preallocate memory.
/// </summary>
/// <param name="PreallocationSize"></param>
public FILECollection()
{
this.svgHash = new HashSet<FILE>();
this.svgHash.Clear();
}
/// <summary>
/// Overload Constructor Preallocates
/// memory to be used for the new
/// FILE Collection.
/// </summary>
public FILECollection(int PreallocationSize, string fileName = "N/A", int fileHashDigestSize = 32)
{
FileName = fileName;
PreallocationList = new List<FILE>(PreallocationSize);
for (int i = 0; i <= PreallocationSize; i++)
{
byte[] buffer = new byte[fileHashDigestSize];
FILE preallocationSVG = new FILE()
{
fileName = "",
folderName = "",
fileHashDigest = buffer
};
PreallocationList.Add(preallocationSVG);
}
this.svgHash = new HashSet<FILE>(PreallocationList);
this.svgHash.Clear(); // Capacity remains unchanged until a call to TrimExcess is made.
}
/// <summary>
/// Add an FILE file to
/// the FILE Collection.
/// </summary>
/// <param name="svg"></param>
public void Add(FILE svg)
{
this.svgHash.Add(svg);
}
/// <summary>
/// Removes all elements
/// from the FILE Collection
/// </summary>
public void Clear()
{
svgHash.Clear();
}
/// <summary>
/// Determine if the FILE collection
/// contains the EXACT FILE file, folder,
/// and byte[] sequence. This guarantees
/// that the collection contains the EXACT
/// file you are looking for.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Contains(FILE item)
{
return svgHash.Any(f => f.fileHashDigest.SequenceEqual(item.fileHashDigest) &&
f.fileName == item.fileName &&
f.folderName == item.folderName);
}
/// <summary>
/// Determine if the FILE collection
/// contains the same file and folder name,
/// byte[] sequence is not compared. The file and folder
/// name may be the same but this does not guarantee the
/// file contents are exactly the same. Use Contains() instead.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool ContainsPartially(FILE item)
{
return svgHash.Any(f => f.fileName == item.fileName &&
f.folderName == item.folderName);
}
/// <summary>
/// Returns the total number
/// of FILE files in the Collection.
/// </summary>
public int Count
{ get { return svgHash.Count(); } }
public bool IsReadOnly
{ get { return true; } }
public void CopyTo(FILE[] array, int arrayIndex)
{
svgHash.CopyTo(array, arrayIndex);
}
public bool Remove(FILE item)
{
return svgHash.Remove(item);
}
public IEnumerator<FILE> GetEnumerator()
{
return svgHash.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return svgHash.GetEnumerator();
}
}
}
I think either I am debugging in a terribly wrong way, or Microsoft should take a look at this. It's like future code is breaking current code...which is impossible!
OK here's my best guess..
First, as I mentioned in the comments, the exception doesn't occur if you comment out the line FILE exists = randomCollection.Where(f => f.fileName == test.fileName && f.fileHashDigest.SequenceEqual(test.fileHashDigest)).First();
Second, I noticed the same behavior can be reproduced with the following code:
if (true)
{
object o;
o = new object();
Func<bool> m = () => o == null;
}
i.e. the cause seems to be related to the variable being used in a lambda expression. So, looking at the same code snippet above in ILSpy I get the following:
Program.<>c__DisplayClass0_0 <>c__DisplayClass0_ = new Program.<>c__DisplayClass0_0();
<>c__DisplayClass0_.o = new object();
Func<bool> func = new Func<bool>(<>c__DisplayClass0_.<Main>b__0);
so my best guess is that the NullReferenceException refers to <>c__DisplayClass0_ intance being null - and I'm therefore inclined to believe that the stepping through the if(true) actually skipped the first line where <>c__DisplayClass0_ is instantiated
I followed this article and I created my CustomSettingsProvider in order to get rid of the "_url_somehash" part of the path where the user.config file is stored. Now my settings are stored in <LocalApplicationData>\CompanyName\ProductName\Version\user.config as I wanted.
My user.config file (written by my application before creating my CustomSettingsProvider) contains one Int32[] property that was stored and loaded correctly by the default SettingsProvider. When I use my CustomSettingsProvider I get the following exception:
Exception InvalidCastException
Source = mscorlib
Message = Invalid cast from 'System.String' to 'System.Int32[]'.
TargetSite = System.Object DefaultToType(System.IConvertible, System.Type, System.IFormatProvider)
Stack =
System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
System.String.System.IConvertible.ToType(Type type, IFormatProvider provider)
System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
System.Convert.ChangeType(Object value, Type conversionType)
MyApp.Interface.CustomSettingsProvider.GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection) in d:\Users\angelo\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Interface\CustomSettingsProvider.cs:line 112
System.Configuration.SettingsBase.GetPropertiesFromProvider(SettingsProvider provider)
System.Configuration.SettingsBase.GetPropertyValueByName(String propertyName)
System.Configuration.SettingsBase.get_Item(String propertyName)
System.Configuration.ApplicationSettingsBase.GetPropertyValue(String propertyName)
System.Configuration.ApplicationSettingsBase.get_Item(String propertyName)
MyApp.Properties.Settings.get_UpgradeRequired() in d:\Users\angelo\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Properties\Settings.Designer.cs:line 31
MyApp.Interface.Program.Run() in d:\Users\angelo\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Interface\Program.cs:line 51
MyApp.Interface.Program.Main() in d:\Users\angelo\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Interface\Program.cs:line 34
How can I fix this problem? In a more general way, how can I store collections and classes in the same way I can do it with the default SettingsProvider?
This is the full code of my CustomSettingsProvider class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
// ==>>>> https://stackoverflow.com/questions/2265271/custom-path-of-the-user-config
// https://stackoverflow.com/questions/1947185/c-sharp-get-special-folder
namespace MyApp.Interface
{
class CustomSettingsProvider : SettingsProvider
{
#region Helper struct
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
#endregion
#region Constants
const string NAME = "name";
const string SERIALIZE_AS = "serializeAs";
const string CONFIG = "configuration";
const string USER_SETTINGS = "userSettings";
const string SETTING = "setting";
#endregion
#region Fields
bool _loaded;
#endregion
#region Properties
/// <summary>
/// Override.
/// </summary>
public override string ApplicationName { get { return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name; } set { /*do nothing*/ } }
/// <summary>
/// The setting key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = #"C:\temp\user.config";</c>
/// </summary>
private string UserConfigPath
{
get
{
System.Diagnostics.FileVersionInfo versionInfo;
string strUserConfigPath, strUserConfigFolder;
strUserConfigPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create);
versionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location);
strUserConfigPath = Path.Combine(strUserConfigPath, versionInfo.CompanyName, versionInfo.ProductName, versionInfo.ProductVersion, "user.config");
strUserConfigFolder = Path.GetDirectoryName(strUserConfigPath);
if(!Directory.Exists(strUserConfigFolder))
Directory.CreateDirectory(strUserConfigFolder);
return strUserConfigPath;
}
}
/// <summary>
/// In memory storage of the settings values
/// </summary>
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
#endregion
#region Constructor
/// <summary>
/// Loads the file into memory.
/// </summary>
public CustomSettingsProvider()
{
SettingsDictionary = new Dictionary<string, SettingStruct>();
}
/// <summary>
/// Override.
/// </summary>
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(ApplicationName, config);
}
#endregion
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if(!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//iterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach(SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if(SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach(SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if(!SettingsDictionary.ContainsKey(value.Name))
SettingsDictionary.Add(value.Name, setting);
else
SettingsDictionary[value.Name] = setting;
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
/// <summary>
/// Loads the values of the file into memory.
/// </summary>
private void LoadValuesFromFile()
{
string strUserConfigPath;
strUserConfigPath = UserConfigPath;
//if the config file is not where it's supposed to be create a new one.
if(!File.Exists(strUserConfigPath))
CreateEmptyConfig(strUserConfigPath);
//System.Security.Policy.StrongName strongName = new System.Security.Policy.StrongName(
//ClickOnce
//load the xml
var configXml = XDocument.Load(UserConfigPath);
//get all of the <setting name="..." serializeAs="..."> elements.
var settingElements = configXml.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName).Elements(SETTING);
//iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
//using "String" as default serializeAs...just in case, no real good reason.
foreach(var element in settingElements)
{
var newSetting = new SettingStruct()
{
name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value,
serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value,
value = element.Value ?? String.Empty
};
SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting);
}
}
/// <summary>
/// Creates an empty user.config file...looks like the one MS creates.
/// This could be overkill a simple key/value pairing would probably do.
/// </summary>
private void CreateEmptyConfig(string strUserConfigPath)
{
Configuration config1;
config1 = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
if(File.Exists(config1.FilePath))
{
File.Copy(config1.FilePath, strUserConfigPath);
}
else
{
string s = Properties.Settings.Default.LastLoadedImage;
var doc = new XDocument();
var declaration = new XDeclaration("1.0", "utf-8", "true");
var config = new XElement(CONFIG);
var userSettings = new XElement(USER_SETTINGS);
var group = new XElement(typeof(Properties.Settings).FullName);
userSettings.Add(group);
config.Add(userSettings);
doc.Add(config);
doc.Declaration = declaration;
doc.Save(strUserConfigPath);
}
}
/// <summary>
/// Saves the in memory dictionary to the user config file
/// </summary>
private void SaveValuesToFile()
{
//load the current xml from the file.
var import = XDocument.Load(UserConfigPath);
//get the settings group (e.g. <Company.Project.Desktop.Settings>)
var settingsSection = import.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName);
//iterate though the dictionary, either updating the value or adding the new setting.
foreach(var entry in SettingsDictionary)
{
var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key);
if(setting == null) //this can happen if a new setting is added via the .settings designer.
{
var newSetting = new XElement(SETTING);
newSetting.Add(new XAttribute(NAME, entry.Value.name));
newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs));
newSetting.Value = (entry.Value.value ?? String.Empty);
settingsSection.Add(newSetting);
}
else //update the value if it exists.
{
setting.Value = (entry.Value.value ?? String.Empty);
}
}
import.Save(UserConfigPath);
}
#region Angelo
private object GetDefaultValue(SettingsProperty setting)
{
if (setting.PropertyType.IsEnum)
return Enum.Parse(setting.PropertyType, setting.DefaultValue.ToString());
// Return the default value if it is set
// Return the default value if it is set
if (setting.DefaultValue != null)
{
System.ComponentModel.TypeConverter tc = System.ComponentModel.TypeDescriptor.GetConverter(setting.PropertyType);
return tc.ConvertFromString(setting.DefaultValue.ToString());
}
else // If there is no default value return the default object
{
return Activator.CreateInstance(setting.PropertyType);
}
}
#endregion
}
}
To read properties that are serialized as XML, you first need to deserialize them.
Probably the easiest way is to add a new method called something like getPropertyValue, which determines whether it should return the string value directly or deserialize it first. Then, in your code shown below, you can just call this method instead of using Convert.ChangeType to set property values:
var t = Type.GetType(setting.PropertyType.FullName);
if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
// value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
value.PropertyValue = getPropertyValue(SettingsDictionary[setting.Name].value, t, setting.SerializeAs);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
// value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
value.PropertyValue = getPropertyValue((string)setting.DefaultValue, t, setting.SerializeAs);
}
An example of how your new method getPropertyValue might work:
private object getPropertyValue(string settingValue, Type settingType, SettingsSerializeAs serializeAs)
{
switch (serializeAs)
{
case SettingsSerializeAs.String:
return settingValue;
case SettingsSerializeAs.Xml:
//for demo purposes, assumes this is your int array--otherwise do further checking to get the correct type
XmlSerializer serializer = new XmlSerializer(typeof(int[]));
return serializer.Deserialize(new StringReader(settingValue));
//implement further types as required
default:
throw new NotImplementedException(string.Format("Settings deserialization as {0} is not implemented", serializeAs));
}
}
This will resolve the invalid cast error and load your array of integers into the setting.
You'll need to apply the corresponding treatment when you save the settings. If you hit complications with that, I suggest you post a new question as the issues are somewhat different.
After serialization sometimes xml files is filled with empty spaces. Why is it happen?
My serialization code is:
XmlSerializer serializer = new XmlSerializer(typeof(SerializableStorage));
using (StreamWriter sw = new StreamWriter(storageName, false, myEncoding))
{
serializer.Serialize(sw, messages);
}
This code serializes me messages into XML.
SerializableStorage looks like:
[XmlRoot("Storage")]
public class SerializableStorage
{
/// <summary>
/// Gets or sets an array of server messages
/// </summary>
[XmlArray("Messages")]
[XmlArrayItem("Message")]
public List<NMessage> Messages
{
get;
protected set;
}
/// <summary>
/// Public constructor
/// </summary>
public SerializableStorage()
{
Messages = new List<NMessage>();
}
}
NMessage class has all fields as public.
And deserialization if needed:
XmlSerializer serializer = new XmlSerializer(typeof(SerializableStorage));
StreamReader reader = new StreamReader(storageName, myEncoding);
StoreMessages = (SerializableStorage)serializer.Deserialize(reader);
reader.Close();
I've attached an "empty xml" example here
I've got a class salesman in the following format:
class salesman
{
public string name, address, email;
public int sales;
}
I've got another class where the user inputs name, address, email and sales.
This input is then added to a list
List<salesman> salesmanList = new List<salesman>();
After the user has input as many salesman to the list as they like, they have the option to save the list to a file of their choice (which I can limit to .xml or .txt(which ever is more appropriate)).
How would I add this list to the file?
Also this file needs to be re-read back into a list if the user wishes to later view the records.
Something like this would work. this uses a binary format (the fastest for loading) but the same code would apply to xml with a different serializer.
using System.IO;
[Serializable]
class salesman
{
public string name, address, email;
public int sales;
}
class Program
{
static void Main(string[] args)
{
List<salesman> salesmanList = new List<salesman>();
string dir = #"c:\temp";
string serializationFile = Path.Combine(dir, "salesmen.bin");
//serialize
using (Stream stream = File.Open(serializationFile, FileMode.Create))
{
var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bformatter.Serialize(stream, salesmanList);
}
//deserialize
using (Stream stream = File.Open(serializationFile, FileMode.Open))
{
var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
List<salesman> salesman = (List<salesman>)bformatter.Deserialize(stream);
}
}
}
I just wrote a blog post on saving an object's data to Binary, XML, or Json; well writing an object or list of objects to a file that is. Here are the functions to do it in the various formats. See my blog post for more details.
Binary
/// <summary>
/// Writes the given object instance to a binary file.
/// <para>Object type (and all child types) must be decorated with the [Serializable] attribute.</para>
/// <para>To prevent a variable from being serialized, decorate it with the [NonSerialized] attribute; cannot be applied to properties.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the XML file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the XML file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToBinaryFile<T>(string filePath, T objectToWrite, bool append = false)
{
using (Stream stream = File.Open(filePath, append ? FileMode.Append : FileMode.Create))
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(stream, objectToWrite);
}
}
/// <summary>
/// Reads an object instance from a binary file.
/// </summary>
/// <typeparam name="T">The type of object to read from the XML.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the binary file.</returns>
public static T ReadFromBinaryFile<T>(string filePath)
{
using (Stream stream = File.Open(filePath, FileMode.Open))
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
return (T)binaryFormatter.Deserialize(stream);
}
}
XML
Requires the System.Xml assembly to be included in your project.
/// <summary>
/// Writes the given object instance to an XML file.
/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [XmlIgnore] attribute.</para>
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToXmlFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
{
TextWriter writer = null;
try
{
var serializer = new XmlSerializer(typeof(T));
writer = new StreamWriter(filePath, append);
serializer.Serialize(writer, objectToWrite);
}
finally
{
if (writer != null)
writer.Close();
}
}
/// <summary>
/// Reads an object instance from an XML file.
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object to read from the file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the XML file.</returns>
public static T ReadFromXmlFile<T>(string filePath) where T : new()
{
TextReader reader = null;
try
{
var serializer = new XmlSerializer(typeof(T));
reader = new StreamReader(filePath);
return (T)serializer.Deserialize(reader);
}
finally
{
if (reader != null)
reader.Close();
}
}
Json
You must include a reference to Newtonsoft.Json assembly, which can be obtained from the Json.NET NuGet Package.
/// <summary>
/// Writes the given object instance to a Json file.
/// <para>Object type must have a parameterless constructor.</para>
/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [JsonIgnore] attribute.</para>
/// </summary>
/// <typeparam name="T">The type of object being written to the file.</typeparam>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the file.</param>
/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
public static void WriteToJsonFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
{
TextWriter writer = null;
try
{
var contentsToWriteToFile = JsonConvert.SerializeObject(objectToWrite);
writer = new StreamWriter(filePath, append);
writer.Write(contentsToWriteToFile);
}
finally
{
if (writer != null)
writer.Close();
}
}
/// <summary>
/// Reads an object instance from an Json file.
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object to read from the file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the Json file.</returns>
public static T ReadFromJsonFile<T>(string filePath) where T : new()
{
TextReader reader = null;
try
{
reader = new StreamReader(filePath);
var fileContents = reader.ReadToEnd();
return JsonConvert.DeserializeObject<T>(fileContents);
}
finally
{
if (reader != null)
reader.Close();
}
}
Example
// Write the list of salesman objects to file.
WriteToXmlFile<List<salesman>>("C:\salesmen.txt", salesmanList);
// Read the list of salesman objects from the file back into a variable.
List<salesman> salesmanList = ReadFromXmlFile<List<salesman>>("C:\salesmen.txt");
If you want to use JSON then using Json.NET is usually the best way to go.
If for some reason you are unable to use Json.NET you can use the built in JSON support found in .NET.
You will need to include the following using statement and add a reference for System.Web.Extentsions.
using System.Web.Script.Serialization;
Then you would use these to Serialize and Deserialize your object.
//Deserialize JSON to your Object
YourObject obj = new JavaScriptSerializer().Deserialize<YourObject>("File Contents");
//Serialize your object to JSON
string sJSON = new JavaScriptSerializer().Serialize(YourObject);
https://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer_methods(v=vs.110).aspx
If you want xml serialization, you can use the built-in serializer. To achieve this, add [Serializable] flag to the class:
[Serializable()]
class salesman
{
public string name, address, email;
public int sales;
}
Then, you could override the "ToString()" method which converts the data into xml string:
public override string ToString()
{
string sData = "";
using (MemoryStream oStream = new MemoryStream())
{
XmlSerializer oSerializer = new XmlSerializer(this.GetType());
oSerializer.Serialize(oStream, this);
oStream.Position = 0;
sData = Encoding.UTF8.GetString(oStream.ToArray());
}
return sData;
}
Then just create a method that writes this.ToString() into a file.
UPDATE
The mentioned above will serialize single entry as xml. If you need the whole list to be serialized, the idea would be a bit different. In this case you'd employ the fact that lists are serializable if their contents are serializable and use the serialization in some outer class.
Example code:
[Serializable()]
class salesman
{
public string name, address, email;
public int sales;
}
class salesmenCollection
{
List<salesman> salesmanList;
public void SaveTo(string path){
System.IO.File.WriteAllText (path, this.ToString());
}
public override string ToString()
{
string sData = "";
using (MemoryStream oStream = new MemoryStream())
{
XmlSerializer oSerializer = new XmlSerializer(this.GetType());
oSerializer.Serialize(oStream, this);
oStream.Position = 0;
sData = Encoding.UTF8.GetString(oStream.ToArray());
}
return sData;
}
}