C# Writing to XML file does not save object - c#

I have some experience in Java. Today I started programming in C# because I like Visual Studio.
As an exercise i'm building a system that manages all employees in a company.
Im working on a class called Function:
public class Function
{
private String functionname;
private String department;
private double hourpay;
public String getFunc()
{
return functionname;
}
public String getDepartement()
{
return department;
}
public double getPay()
{
return hourpay;
}
public String toString()
{
return ("Function: " + functionname + "\n" + "Department: " + department + "\n" + "Hourly Pay: " + hourpay);
}
public void setFunctionName(string functionname)
{
this.functionname = functionname;
}
public void setDepartment(String department)
{
this.department = department;
}
public void setPay(double pay)
{
this.hourpay = pay;
}
A very simple basic class to model a function, Now I would like to save functions in a XML file.
What I have in mind is:
After I created a function I put it in a list called Functions (List functions)
I write the list to an XML file.
If I want to update the XML file I simply load the List, add a new function, and then overwrite it to the XML file.
I made my class functions like this;
public class Functions{
public List<Function> functions = new List<Function>();
public void addFunction(Function func){
functions.Add(func);
}
public void writeFunctions()
{
System.Xml.Serialization.XmlSerializer writer =
new System.Xml.Serialization.XmlSerializer(typeof(Functions));
System.IO.StreamWriter file = new System.IO.StreamWriter(
#"C:\CDatabase\Functions.xml");
writer.Serialize(file, this);
file.Close();
}
}
To test it I implemented this in a click event of a button:
Function programmer = new Function();
schoonmaker.setFunctionName("Programmer");
schoonmaker.setDepartment("IT");
schoonmaker.setPay(16.50);
FunctionDatabase.addFunction(schoonmaker);
FunctionDatabase.writeFunctions();
It creates the XML file(If its not there), and this is in the XML file:
<Functions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<functions>
<Function />
</functions>
</Functions>
Its empty, and I dont understand why and how to fix it.
I think i'm thinking too easy, but I cant find a fix.
Thanks in advance,
Chris

C# doesn't use getter and setter methods like Java. Create your properties like this:
private string functionname;
public string functionname
{
get { return functionname; }
set { functionname = value; }
}

A suggestion would be to use properties instead of getter/setter functions - they are a feature of C# which are used a lot.
For example, instead of:
public double getPay()
{
return hourpay;
}
Try:
public double HourPay { get; set; }
Or if you want to be able to perform actions within the getter/setter:
private double hourPay;
public double HourPay
{
get
{
return hourPay;
}
set
{
hourPay = value;
}
}
By using public properties in this way, the XML serializer should generate the file you were expecting.

Related

Saving/Reloading user entry (textbox values, names) in C# WPF - Visual Studio 2017

I would like to ask if you could direct me towards a solution to my problem. What I would like to do is that the user would input name of patient and some values in the textboxes (weight, protein, etc) and these would be stored somewhere. The name/patient would be reloaded with the values and you could edit them and save again. There would be a list of patients (from drop-down menu for example). I wanted to post a screenshot of my app, but I need more reputation.
How can I achieve this in a simple way? Is it what databases are for or is there something similar? I tried SQLite, but it's not working with VS 2017. What about other embedded databases or serialization?
If databases are solution, I am fine with server-less ones.
Thank you very much![enter image description here]
You could try the SQLite-Net library to store the values in an SQLite database. It has a good tutorial in the README.
I figured out a part of my question.
I used XML method where I take whatever is in the textboxes and store it in XML file that is named aftere that particular person. Code:
public class SaveXML
{
public static void SaveData(object obj, string filename)
{
XmlSerializer sr = new XmlSerializer(obj.GetType());
TextWriter writer = new StreamWriter(filename);
sr.Serialize(writer, obj);
writer.Close();
}
}
public class Information
{
private string txtdata1;
private string txtdata2;
private string txtdata3;
public string PatientName
{
get { return txtdata1; }
set { txtdata1 = value; }
}
public string PatientWeight
{
get { return txtdata2; }
set { txtdata2 = value; }
}
public string PatientTFIWeight
{
get { return txtdata3; }
set { txtdata3 = value; }
}
}
private void Save_Click(object sender, RoutedEventArgs e)
{
try
{
Information info = new Information();
info.PatientName = Patient.Text;
info.PatientWeight = Weight.Text;
info.PatientTFIWeight = TFIWeight.Text;
SaveXML.SaveData(info, Patient.Text);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

C# Parameters class with app.config

I have a configuration class with all the parameters of my application, to acquire images from a scanner.
I have parameters like color/BW, resolution...
The parameters are changed often, so I'm searching a solution to write automatically when I save the parameters the changed parameters in the app.config file. And to do the reverted thing, write my class from the app.config at the init of the software.
Here are my two classes :
private void GetParameters() {
try
{
var appSettings = ConfigurationManager.AppSettings;
Console.WriteLine( ConfigurationManager.AppSettings["MyKey"]);
if (appSettings.Count == 0)
{
Console.WriteLine("AppSettings is empty.");
}
else
{
foreach (var key in appSettings.AllKeys)
{
Console.WriteLine("Key: {0} Value: {1}", key, appSettings[key]);
}
}
}
catch (ConfigurationErrorsException)
{
MessageBox.Show("Error reading app settings");
}
}
private void SetParameters(string key, string value)
{
try
{
Configuration configManager = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
KeyValueConfigurationCollection confCollection = configManager.AppSettings.Settings;
if (confCollection[key] == null)
{
confCollection.Add(key, value);
}
else
{
confCollection[key].Value = value;
}
configManager.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(configManager.AppSettings.SectionInformation.Name);
}
catch (ConfigurationErrorsException)
{
MessageBox.Show("Error writing app settings");
}
}
I don't want to call the method for every parameter...
And there is my parameters class :
class ScannerParameters
{
public bool Color { get; set; }
public int Resolution{ get; set; }
public string FilePath { get; set; }
public TypeScan TypeScan { get; set; }
public string TextTest{ get; set; }
}
The question can be translated into how do I save an object into some kind of persistence?
Either use a database (seems like an overkill) or serialize it using a serializer or simply write it all down into a text file yourself. Using json serialization, serializing your ScannerParameters and then writing that into a file would seem most appropriate.
Using newtonsoft json, which is defacto standard for .net there's nice examples # http://www.newtonsoft.com/json/help/html/SerializingJSON.htm
In your case you would do:
// our dummy scannerParameters objects
var parameters = new ScannerParameters();
// let's serialize it all into one string
string output = JsonConvert.SerializeObject(paramaters);
// let's write all that into a settings text file
System.IO.File.WriteAllText("parameters.txt", output);
// let's read the file next time we need it
string parametersJson = System.IO.File.ReadAllText("parameters.txt);
// let's deserialize the parametersJson
ScannerParameters scannerParameters = JsonConvert.DeserializeObject<ScannerParameters>(parametersJson);

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.

XmlSerializer; System.invalidOperationException

EDIT: Using the code provided by Jon Skeet.
I get the following error:
Message: There is an error in XML document (2, 2).
Inner Exception: {"<Translator xmlns=''> was not expected."}
If it helps I can provide the code below:
Translator.cs:
public class Translator
{
public FullBotTranslation Translation;
public Translator()
{
Translation = new FullBotTranslation();
}
public void LoadLanguage(string language)
{
if (!Useful.ExistFile(System.AppDomain.CurrentDomain.BaseDirectory + "\\LanguagePacks\\" + language + ".xml"))
language = "Francais";
Translation = XmlSerializerHelper.Deserialize<FullBotTranslation>(System.AppDomain.CurrentDomain.BaseDirectory + "\\LanguagePacks\\" + language + ".xml");
}
public string GetTranslation(PhraseID phraseId)
{
foreach (Phrase phrase in Translation.Phrases)
{
if (phrase.PhraseID == phraseId)
return phrase.PhraseString;
}
return "Incomplete translation...";
}
#region Nested type: Translation
[Serializable]
public class FullBotTranslation
{
public List<Phrase> Phrases = new List<Phrase>();
}
#endregion
}
Phrase.cs:
public class Phrase
{
public PhraseID PhraseID { set; get; }
public string PhraseString{ set; get; }
public Phrase()
{
}
}
PhraseID.cs
[Serializable]
public enum PhraseID
{
none,
Button_Start,
Button_Stop,
}
I use it like this:
Setup:
private Translator _translator;
_translator = new Translator();
Saving:
Helpers.XmlSerializerHelper.Serialize(
System.AppDomain.CurrentDomain.BaseDirectory + "\\LanguagePacks\\" + langPackName.Text + ".xml",
_translator);
Loading:
_translator = new Translator(); //yes this is needed ;)
_translator.LoadLanguage(preloadedLangCombo.SelectedItem.ToString());
When using the code above to save an XML file it outputs the following:
English.XML:
<?xml version="1.0"?>
<Translator xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Translation>
<Phrases>
<Phrase>
<PhraseID>none</PhraseID>
<PhraseString>Incomplete Translation</PhraseString>
</Phrase>
<Phrase>
<PhraseID>Button_Start</PhraseID>
<PhraseString>Start</PhraseString>
</Phrase>
<Phrase>
<PhraseID>Button_Stop</PhraseID>
<PhraseString>Stop</PhraseString>
</Phrase>
</Phrases>
</Translation>
</Translator>
It's not clear what's going wrong here - particularly because the error message doesn't seem to match your sample XML. Your exception handling may well be hiding problems though - it's a really bad idea to catch all exceptions like that, and you're going to unnecessary lengths to close the streams involved. I would condense your class down to just this:
public static class XmlSerializerHelper
{
public static void Serialize(String path, object #object)
{
using (var stream = File.Create(path))
{
var s = new XmlSerializer(#object.GetType());
s.Serialize(stream, #object);
}
}
public static T Deserialize<T>(String path)
{
using (var stream = File.OpenRead(path))
{
var s = new XmlSerializer(typeof(T));
return (T) s.Deserialize(stream);
}
}
}
Now if something goes wrong, the exception will propagate out of the methods, rather than being disguised. Also, note that I've serialized to/from just streams, rather than getting a StreamWriter involved. It's simpler to let the XML infrastructure deal with all the encoding.
The above code works fine for me in a simple test.

Parse/Extract information from text template

i need to extract information from incoming (e.g. xml) data based on a given template.
The template may be XML or plain text (comma separated). For each type of message there exists a template, e.g.
<SomeMessage>
<Id>$id</Id>
<Position>
<X>$posX</X>
<Y>$posY</Y>
<Z>$posZ</Z>
</Position>
</SomeMessage>
The incoming data for example is:
<SomeMessage>
<Id>1</Id>
<Position>
<X>0.5f</X>
<Y>1.0f</Y>
<Z>0.0f</Z>
</Position>
</SomeMessage>
Now i need to extract information about $id, $posX, etc.
Parser p = new Parser(templateString);
int id = p.Extract("id", incomingString);
float posx = p.Extract("posX", incomingString);
I need something like the difference of incomingData and template and then extract the information at the appropiate position. Because there exist several tempaltes which contain different information and may be extended in the future i am looking for a general approach.
The template in this case may also be
$id,$posX,$posY,$posZ
and the incoming data would be then
1,0.5f,1.0f,0.0f
The latter case may be eaiser to parse, but i need a solution which is able the handle both (xml template as well as non xml).
You could create a parsing class having a property for each field:
class Parser
{
public string PositionX { get; set; }
public string PositionY { get; set; }
public string PositionZ { get; set; }
public Parser(XmlNode item)
{
this.PositionX = GetNodeValue(item, "Position/X");
this.PositionY = GetNodeValue(item, "Position/X/Y");
this.PositionZ = GetNodeValue(item, "Position/X/Y/Z");
}
}
I can supply a routine that can generate such parsing classes from sample xml if your interested, when arrays do not concern. GetNodeValue is a method that uses an xpath query and returns the value for the xpath (basicly XmlNode.SelectSingleNode with some added parsing added to it).
It would probably be a good idea to use a Interface and 2 different templates for each occasion. Note that the returned Message is not completed but it gives you an idea. With the static XElement.Parse you can parse well formed XML strings for easier usage.
public interface IParser
{
Message Parse(String Payload);
}
// Position Class
public class Position
{
public int X { get; private set; }
public int Y { get; private set; }
public int Z { get; private set; }
public Position(int X, int Y, int Z)
{
this.X = X;
this.Y = Y;
this.Z = Z;
}
}
// Message Class
public class Message
{
public String ID { get; private set; }
public Position Position { get; private set; }
public Message(String ID, Position Position)
{
this.ID = ID;
this.Position = Position;
}
}
// Parser Class
public class XMLParser : IParser
{
public Message Parse(string Payload)
{
var result = XElement.Parse(Payload);
return new Message(result.Elements().ElementAt(0).Value, new Position(X,Y,Z);
}
}
For each template create a parser definition file of the format:
Parser Type (XML or CSV)
Variable1, path
variable2, path
etc
for xml path might be someMessage,Position,x.
for csv you might forget the path and just list the variables in order.
Then when you read in your template file your pick up the parser type and the path to each of the variables. If you have a lot of hierarchical information then you'll have to apply a bit of imagination to this but it should be fine for the simple case you've given.
For anything over CSV you will probably have to use a parser, but XML/XPATH is pretty simple to find the basics for.
using System;
using System.IO;
using System.Xml;
class TemplateParse {
XmlDocument xdoc;
string GetPath(XmlNode node, string val, string path){
if(node.HasChildNodes){
if(node.ChildNodes.Count == 1 && node.FirstChild.NodeType == XmlNodeType.Text)
return (node.FirstChild.Value == val) ? path + "/" + node.Name : String.Empty;
foreach(XmlNode cnode in node.ChildNodes){
if(cnode.NodeType != XmlNodeType.Element) continue;
string result = GetPath(cnode, val, path + "/" + node.Name);
if(result != String.Empty) return result;
}
}
return "";
}
public TemplateParse(string templateXml){
xdoc = new XmlDocument();
xdoc.LoadXml(templateXml);
}
public string Extract(string valName, string data){
string xpath = GetPath((XmlNode)xdoc.DocumentElement, "$" + valName, "/");
var doc = new XmlDocument();
doc.LoadXml(data);
return doc.SelectSingleNode(xpath).InnerText;
// var value = doc.SelectSingleNode(xpath).InnerText;
// var retType = typeof(T);
// return (T)retType.InvokeMember("Parse", System.Reflection.BindingFlags.InvokeMethod, null, null, new []{value});
}
}
class Sample {
static void Main(){
string templateString = File.ReadAllText(#".\template.xml");
string incomingString = File.ReadAllText(#".\data.xml");
var p = new TemplateParse(templateString);
string[] names = new [] { "id", "posX", "posY", "posZ" };
foreach(var name in names){
var value = p.Extract(name, incomingString);
Console.WriteLine("{0}:{1}", name, value);
}
}
}
OUTPUT
id:1
posX:0.5f
posY:1.0f
posZ:0.0f

Categories