Store custom application settings in XML - c#

please help. I have this code, it's my class to serialize\deserialize application settings.
[XmlRoot("EvaStartupData")]
[Serializable]
public class MyConfigClass
{
public string ServerName { get; set; }
public string Database { get; set; }
public string UserName { get; set; }
public string UserLogin { get; set; }
public static void MyConfigLoad()
{
FileInfo fi = new FileInfo(myConfigFileName);
if (fi.Exists)
{
XmlSerializer mySerializer = new XmlSerializer(myConfigClass.GetType());
StreamReader myXmlReader = new StreamReader(myConfigFileName);
try
{
myConfigClass = (MyConfigClass)mySerializer.Deserialize(myXmlReader);
myXmlReader.Close();
}
catch (Exception e)
{
MessageBox.Show("Ошибка сериализации MyConfigLoad\n" + e.Message);
}
finally
{
myXmlReader.Dispose();
}
}
}
public static void MyConfigSave()
{
XmlSerializer mySerializer = new XmlSerializer(myConfigClass.GetType());
StreamWriter myXmlWriter = new StreamWriter(myConfigFileName);
try
{
mySerializer.Serialize(myXmlWriter, myConfigClass);
}
catch (Exception e)
{
MessageBox.Show("Ошибка сериализации MyConfigSave\n" + e.Message);
}
finally
{
myXmlWriter.Dispose();
}
}
}
Serialization give's me simple xml-structure:
<ServerName>navuhodonoser</ServerName>
<Database>matrix</Database>
<UserName>Mr.Smith</UserName>
<UserLogin>neo</UserLogin>
How must i modify my class to get this xml structure ?:
<Connection ServerName="navuhodonoser" Database="matrix" ....>

By default the XmlSerializer will serialize all public properties as elements; to override that you'll need to tag each property with [XmlAttribute] (from System.Xml.Serialization namespace) which will give you your desired output.
For example:
[XmlAttribute]
public string ServerName { get; set; }
[XmlAttribute]
public string Database { get; set; }
[XmlElement]
public string UserName { get; set; }
// Note: no attribute
public string UserLogin { get; set; }
will produce something like:
<xml ServerName="Value" Database="Value">
<UserName>Value</UserName> <!-- Note that UserName was tagged with XmlElement, which matches the default behavior -->
<UserLogin>Value</UserLogin>
</xml>

I have a couple of suggestions. Try code more like this:
public static void MyConfigLoad()
{
if (!File.Exists(myConfigFileName))
{
return;
}
XmlSerializer mySerializer = new XmlSerializer(myConfigClass.GetType());
using (StreamReader myXmlReader = new StreamReader(myConfigFileName))
{
try
{
myConfigClass = (MyConfigClass)mySerializer.Deserialize(myXmlReader);
}
catch (Exception e)
{
MessageBox.Show("Ошибка сериализации MyConfigLoad\n" + e.ToString());
}
}
}
public static void MyConfigSave()
{
XmlSerializer mySerializer = new XmlSerializer(myConfigClass.GetType());
using (StreamWriter myXmlWriter = new StreamWriter(myConfigFileName))
{
try
{
mySerializer.Serialize(myXmlWriter, myConfigClass);
}
catch (Exception e)
{
MessageBox.Show("Ошибка сериализации MyConfigSave\n" + e.ToString());
}
}
}
You should put the StreamReader and StreamWriter in using blocks so that they will be disposed even if an exception occurs. Also, I suggest you always display e.ToString() instead of just e.Message, as it will display the entire exception, including any inner exceptions.
Also, File.Exists works just like FileInfo.Exists, but doesn't require you to create an instance before using it.
One final note is that you should look into using the Settings feature instead of creating your own configuration classes. That allows you to easily create type-safe settings that can be used throughout your application, and which can be per-user or per-application.

Related

C# Can i implement interface with IEnumerable object and struct?

So I want to write an interface, which should be able to be implemented with any data. This is interface i wrote till now. The reason I chose IEnumerable is because I need to give class Computer or struct Processor
public interface IData<T> where T : IEnumerable<object>
{
public T ReadData();
public void WriteData(T list);
}
And I have two different datas, one is Computer, which is a class. And the other one is Processor (struct)
public struct Processor
{
public string Name { get; set; }
public string AmazonLink { get; set; }
public string AmazonBin { get; set; }
public Processor(string name, string link)
{
Name = name;
try
{
//constructor parses elements which is needed to generate AmazonURL in URLGenerator project
AmazonLink = link.Substring(0, link.IndexOf("&dc"));
string binStart = link.Substring(link.IndexOf("bin%") + 4);
AmazonBin = "%7C" + binStart.Substring(2);
}
catch (Exception e)
{
throw new InnerCustomException("Erorr occured while trying to substring the link", e);
}
}
I tried to do that like this, but it seems like I am not allowed to do that because of boxing?
public class ProcessorServiceCSV : IData<IEnumerable<Processor>>
{ private string Path { get; set; }
private FileMode Filemode { get; set; }
public ProcessorServiceCSV(string path, FileMode fileMode)
{
Path = path;
Filemode = fileMode;
}
//reads Processor list from CSV file
public IEnumerable<Processor> ReadData()
{
try
{
using (var reader = new StreamReader(Path))
using (var csv = new CsvReader(reader))
{
csv.Configuration.CultureInfo = CultureInfo.InvariantCulture;
csv.Configuration.Delimiter = ",";
csv.Configuration.RegisterClassMap<ProcessorMap>();
var records = csv.GetRecords<Processor>().ToList();
return records.ToList();
}
}
catch (FileNotFoundException)
{
throw new DataCustomException("File not found", this);
}
catch (Exception e)
{
throw new DataCustomException("Something's wrong happened:" + e.Message, this);
}
} public void WriteData(IEnumerable<Processor> processors)
{
try
{
using (var stream = File.Open(Path, Filemode))
using (StreamWriter sw = new StreamWriter(stream))
using (CsvWriter cw = new CsvWriter(sw))
{
foreach (Processor processor in processors)
{
cw.Configuration.RegisterClassMap<ProcessorMap>();
cw.WriteRecord<Processor>(processor);
cw.NextRecord();
}
}
}
catch (FileNotFoundException)
{
throw new DataCustomException("File not found", this);
}
catch (FileLoadException)
{
throw new DataCustomException("File could not be opened", this);
}
catch (Exception e)
{
throw new DataCustomException("Something's wrong happened:" + e.Message, this);
}
}
}
}
I know I could change Processor from struct to class, but is it possible to keep struct? Thank you in advance
You have a lot of other problems, including not giving us a complete, working bit of code.
However, it looks like you should be able to do what you want to do if you use an Interface for the Processor struct instead of the actual struct type.
Also, notice how I changed the type for T in your classes. You don't need IEnumerable in your T constraint. I did delete some of your code to get it to somewhat work (the exception in the struct constructor, e.g.), so you will need to do some more work here.
public interface IData<T>
{
IEnumerable<T> ReadData();
void WriteData(IEnumerable<T> list);
}
public interface IProcessor {
string Name { get; set; }
string AmazonLink { get; set; }
string AmazonBin { get; set; }
}
public struct Processor : IProcessor
{
public string Name { get; set; }
public string AmazonLink { get; set; }
public string AmazonBin { get; set; }
public Processor(string name, string link)
{
Name = name;
//constructor parses elements which is needed to generate AmazonURL in URLGenerator project
AmazonLink = link.Substring(0, link.IndexOf("&dc"));
string binStart = link.Substring(link.IndexOf("bin%") + 4);
AmazonBin = "%7C" + binStart.Substring(2);
}
}
public class ProcessorServiceCSV<T> : IData<T> where T: IProcessor
{ private string Path { get; set; }
private FileMode Filemode { get; set; }
public ProcessorServiceCSV(string path, FileMode fileMode)
{
Path = path;
Filemode = fileMode;
}
//reads Processor list from CSV file
public IEnumerable<T> ReadData()
{
try
{
using (var reader = new StreamReader(Path))
using (var csv = new CsvReader(reader))
{
csv.Configuration.CultureInfo = CultureInfo.InvariantCulture;
csv.Configuration.Delimiter = ",";
csv.Configuration.RegisterClassMap<ProcessorMap>();
var records = csv.GetRecords<Processor>().ToList();
return records.ToList();
}
}
catch (FileNotFoundException)
{
throw new DataCustomException("File not found", this);
}
catch (Exception e)
{
throw new DataCustomException("Something's wrong happened:" + e.Message, this);
}
}
}
Is this the basic skeleton code of what you are trying to do? Note that the generic collection is IEnumerable<T> and not IEnumetable<object> and hence I updated your IData<T> definition
public class Computer
{
}
public struct Processor
{
}
public interface IData<T>
{
IEnumerable<T> ReadData();
void WriteData(IEnumerable<T> list);
}
public class ComputerData : IData<Computer>
{
public IEnumerable<Computer> ReadData()
{
throw new NotImplementedException();
}
public void WriteData(IEnumerable<Computer> list)
{
throw new NotImplementedException();
}
}
public class ProcessorData : IData<Processor>
{
public IEnumerable<Processor> ReadData()
{
throw new NotImplementedException();
}
public void WriteData(IEnumerable<Processor> list)
{
throw new NotImplementedException();
}
}
Please indicate if this code meets your requirements, and if not why.

Unable to convert XML to C# Object List

I'm trying to convert the XML data to an object list, but it throws an error.
XML
<?xml version="1.0" encoding="utf-8" ?>
<Servers>
<Server>
<ServerName>STAGING</ServerName>
<ServerIP>XXX.XXX.XX.X</ServerIP>
</Server>
</Servers>
C#
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(List<ServerDetails>));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
List<ServerDetails> list = (List<ServerDetails>)serializer.Deserialize(stream);
//Exception here
}
}
ERROR
Inner Exception : <Servers xmlns=''> was not expected.
There is an error in XML document (2,2)
I tried adding the [Serializabe] and [XMLElement] attributes to the class,
and also xmlns="http://www.example.com/xsd/ServerDetails" in the XML
but that did not help.
You have ServerDetails as your class name and in the xml the tag name is different, Try something like this.
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
public class ServerList
{
[XmlArray("Servers")]
[XmlArrayItem("Server", Type = typeof(ServerDetails))]
public ServerDetails[] Servers { get;set;}
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(ServerList));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
var list = (ServerList)serializer.Deserialize(stream);
//Exception here
}
}
I used to use XmlSerializer a lot, but I totally stopped using it because you are forced to create your object structure fitting the xml structure. That makes it hard to maintain. Also XmlSerializer has some serious memory leaks.
If you don't mind, I would suggest to switch to XElement
public IEnumerable<ServerDetails> GetServers(string file)
{
using (var stream = File.Open(file, FileMode.Open, FileAccess.Read))
return GetServers(stream);
}
public IEnumerable<ServerDetails> GetServers(Stream stream)
{
var root = XElement.Load(stream);
return GetServers(root);
}
public IEnumerable<ServerDetails> GetServers(XElement root)
{
foreach (var server in root.Elements("Server"))
{
yield return new ServerDetails
{
ServerName = (string)server.Element("ServerName"),
ServerIP = (string)server.Element("ServerIP"),
};
}
}
Please note that you have to reference System.Xml.Linq
For your convenience here is a test case.
[TestMethod]
public void CanReadServers()
{
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + #"
<Servers>
<Server>
<ServerName>STAGING</ServerName>
<ServerIP>XXX.XXX.XX.X</ServerIP>
</Server>
</Servers>";
IEnumerable<ServerDetails> servers;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
servers = GetServers(stream).ToList();
Assert.AreEqual(1, servers.Count());
Assert.AreEqual("STAGING", servers.ElementAt(0).ServerName);
Assert.AreEqual("XXX.XXX.XX.X", servers.ElementAt(0).ServerIP);
}
Use XmlElement Notation to specify the element name.
public class Servers
{
[XmlElement("Server")]
public ServerDetails[] ServersDetails { get; set; }
}
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(Servers));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
Servers list = (Servers)serializer.Deserialize(stream);
//Exception here
}
}

My nested Class collection XMLRoot name is not being used when serializing to xml

I have a Model populated and I wish to serlise to an xml document.
Due to naming conventions I have to over ride the class names for my XML document,
This is my Model(s):
[Serializable]
[XmlRoot("preferences")]
public class PreferencesModel
{
[XmlIgnore]
public string MessageToUser { get; set; }
[XmlElement(ElementName = "sectiondivider")]
public List<SectionDivider> SectionDivider { get; set; }
}
[Serializable]
[XmlRoot(ElementName = "sectiondivider")]
public class SectionDivider
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("preference")]
public List<PreferenceModel> PreferenceModel { get; set; }
}
[Serializable]
[XmlRoot("preference")]
public class PreferenceModel
{
[XmlAttribute("type")]
public string Type { get; set; }
public string Name { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
[XmlElement("options")]
public List<Option> Options { get; set; }
}
this is how I serialize:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(ObjectToXmlString(obj, includeNameSpace, includeStartDocument, rootAttribute));
return xDoc;
public static string ObjectToXmlString(Object obj, bool includeNameSpace, bool includeStartDocument, XmlRootAttribute rootAttribute)
{
SpecialXmlWriter stWriter = null;
XmlSerializer xmlSerializer = default(XmlSerializer);
string buffer = null;
try
{
if (rootAttribute == null)
{
xmlSerializer = new XmlSerializer(obj.GetType());
}
else
{
xmlSerializer = new XmlSerializer(obj.GetType(), rootAttribute);
}
MemoryStream memStream = new MemoryStream();
StringWriter writer = new StringWriter();
stWriter = new SpecialXmlWriter(memStream, new UTF8Encoding(false), includeStartDocument);
if (!includeNameSpace)
{
System.Xml.Serialization.XmlSerializerNamespaces xs = new XmlSerializerNamespaces();
//To remove namespace and any other inline
//information tag
xs.Add("", "");
xmlSerializer.Serialize(stWriter, obj, xs);
}
else
{
xmlSerializer.Serialize(stWriter, obj);
}
buffer = Encoding.UTF8.GetString(memStream.ToArray());
}
catch (Exception e)
{
string msg = e.Message;
throw;
}
finally
{
if (stWriter != null)
stWriter.Close();
}
return buffer;
}
I call it like this:
XmlDocument preferencesxml = Codec.ObjectToXml(m.SectionDivider,false,
false, new XmlRootAttribute("preferences"));
My m value is:
and my resulting XML is this:
XmlRootAttribute, as the name suggests, only applies to the root element of the XML being serialised.
You need to use XmlTypeAttribute in this context:
[XmlType("sectiondivider")]`
public class SectionDivider
{
//...
}
As an aside, the [Serializable] attribute is not relevant to XmlSerializer - it can be removed unless you need it for some other purpose.

RoundtripKind mode xml serialize

I am using the code below in order to serialize Topology class to xml:
public static bool WriteTopologyFile(string path)
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(Topology));
using (StreamWriter reader = new StreamWriter(path))
{
serializer.Serialize(reader, Runtime.Topology);
}
return true;
}
catch (Exception ex)
{
Log.WriteEventLog(ex, EventLogEntryType.Error);
}
return false;
}
In Topology class, there are some DateTime fields and i want to serialize these DateTime fields in
System.Xml.XmlDateTimeSerializationMode.RoundtripKind
mode. How can i do that?
[XmlIgnore]
public DateTime Time { get; set; }
[XmlElement("Time")]
public string strTime
{
get { return Time.ToString("o"); }
set { Time = DateTime.Parse(value); }
}

Derived Class Deserialization

I have a problem with deserialization with my logic simulation program.
Here are my element classes:
public class AndGateData : TwoInputGateData
{
}
public class TwoInputGateData : GateData
{
public TwoInputGateData()
{
Input2 = new InputData();
Input1 = new InputData();
}
public InputData Input1 { get; set; }
public InputData Input2 { get; set; }
}
public class GateData : ElementData
{
public GateData()
{
OutputData = new OutputData();
}
public OutputData OutputData { get; set; }
}
public class ElementData
{
public int Delay { get; set; }
public Guid Id { get; set; }
}
And here are classes responsible for sockets:
public class InputData : SocketData
{
}
public class SocketData
{
public Guid Id { get; set; }
public SignalData SignalData { get; set; }
}
SignalData is not important here. So, I won't write it (in order to keep this question clean) here unless somebody says it is necessary.
CircuitData is very important:
[XmlRoot("Circuit")]
public class CircuitData
{
[XmlElement(typeof(AndGateData))]
[XmlElement(typeof(OrGateData))]
public List<ElementData> elements = new List<ElementData>();
public List<WireData> wires = new List<WireData>();
public void AddElement(ElementData element)
{
elements.Add(element);
}
public void AddWire(WireData wire)
{
wires.Add(wire);
}
}
Wires are not important right now.
Now, I have written some Serialization:
public class CircuitDataWriter
{
public static void Write(object obj, string fileName)
{
var xmlFormat = new XmlSerializer(typeof(CircuitData));
using(Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None) )
{
xmlFormat.Serialize(fStream,obj);
}
Console.WriteLine("Circuit saved in XML format.");
}
}
It works just like I wanted, it produces that xml document:
<?xml version="1.0"?>
-<Circuit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-<AndGateData>
<Delay>10</Delay>
<Id>bfee6dd7-5946-4b7b-9d0b-15d5cf60e2bf</Id>
-<OutputData> <Id>00000000-0000-0000-0000-000000000000</Id> </OutputData>
-<Input1> <Id>7c767caf-79a9-4c94-9e39-5c38ec946d1a</Id> <SignalData xsi:type="SignalDataOn"/> </Input1>
-<Input2> <Id>d2cad8f8-8528-4db3-9534-9baadb6a2a14</Id> <SignalData xsi:type="SignalDataOff"/> </Input2>
</AndGateData>
<wires/>
</Circuit>
But I have problem with my DESERIALIZATION. Here is the code:
public static CircuitData Read()
{
var reader = new XmlSerializer(typeof(CircuitData));
StreamReader file = new StreamReader("Circuit.xml");
var returnCircuitData = new CircuitData();
returnCircuitData = (CircuitData) reader.Deserialize(file);
return returnCircuitData;
}
Now, it deserializes my Circuit.xml to object, but this object only contains Id and Delay, it does not contain Input1, Input2 or Output. So, it is treated like Element, not like AndGate. I tried to solve it out for a day but it seems that no one has that kind of problem.
I have a suggestion for you, make the Write method generic like this and create the serializer using objectToSerialize.GetType():
public static void Write<T>(T objectToSerialize, string fileName)
{
var xmlSerializer = new XmlSerializer(objectToSerialize.GetType());
...
}
The XmlSerializer.Deserialize() method returns object, you can make your Read method generic like this:
public static T Read<T>(string fileName)
{
var serializer = new XmlSerializer(typeof(T));
using (StreamReader file = new StreamReader(fileName))
{
return (T)serializer.Deserialize(file);
}
}
Other than that you might want to read about:
XmlInclude that is used when you serialize derived classes.
XmlArray and XmlArrayItem that are used for controlling serialization of arrays

Categories

Resources