Setting an XML attribute in the model - c#

I need to set an attribute in my XML. I require the following:
<finAccount version="1.00">
Here is my model so far
[XmlAttribute("version")]
[XmlType("finPOWERConnect")]
public class ApplicationData
{
public List<Account> Accounts;
}
[XmlType("finAccount")]
public class Account
{
//Account stuff
}
The following function serialises my object to xml using the above model.
public Boolean SerialiseObjectToXmlString(Object obj, ref string xml)
{
System.IO.MemoryStream ms = null;
bool Ok = true;
XmlSerializer xmlSerializer = null;
xml = "";
//Serialise
try
{
xmlSerializer = new XmlSerializer(obj.GetType());
ms = new MemoryStream();
xmlSerializer.Serialize(ms, obj);
xml = System.Text.Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
ms.Close();
}
catch(Exception ex)
{
Ok = false;
xml = ex.Message;
}
finally
{
if (ms != null) ms.Dispose();
}
return Ok;
}
I have looked at several examples that set the attribute in the above method however I use this method all throughout the application. Is there a way to set the xml attribute (version="1.00) in the model?

Try this. Class should be XmlRoot and arrays should be XmlElement. The XmlElement avoids adding two layer of the same tag to code. Try without XmlElement and you will see the difference.
[XmlRoot("finPOWERConnect")]
public class ApplicationData
{
[XmlElement("finAccount")]
public List<Account> Accounts {get; set; }
}
[XmlRoot("finAccount")]
public class Account
{
[XmlAttribute("version")]
public string Version { get; set; }
//Account stuff
}
​

Related

Serialization: How to Deserialize in C#

How can I deserialize the string based on what I have done in this method? Basically, what I have here is to pass the string through the network using serialization and deserialize the string in order to convey the message. But once I managed to receive the message, I have no idea if what I'm doing is correct. Here's the code:
string ConvertToString(FrogGame np, Frog1 pm, Frog2 pm2) //Serialization. the three parameters are the classes.
{
XmlSerializer sendSerializer = new XmlSerializer(typeof(FrogGame),new Type[]{typeof(Frog1),typeof(Frog2)});
StreamWriter myWriter = new StreamWriter(#"pad1.xml");
sendSerializer.Serialize(myWriter, np);
sendSerializer.Serialize(myWriter, pm);
sendSerializer.Serialize(myWriter, pm2);
return myWriter.ToString();
} //Overall, I serialize it into string
Once I pass the string through the network, I want to deserialize it in order the pass the message to the classes. How do I continue here onwards? How can I edit? The code:
void StringReceived(string str) //so str is myWriter.ToString()
{
XmlSerializer revSerializer = new XmlSerializer(typeof(FrogGame), new Type[] { typeof(Frog1), typeof(Frog2) });
FileStream myFileStream = new FileStream(#"pad1.xml", FileMode.Open);
FrogGame b = (FrogGame)revSerializer.Deserialize(myFileStream);
if (b is Frog1)
{
if (Network.IsServer())
{
pm = (Frog1)b;
pm.Position.Y = b.pm.Position.Y;
pm.Position.X = b.pm.Position.X;
}
else
{
System.Diagnostics.Debug.WriteLine("BAD Message: " + msg);
}
}
else if (b is Frog2)
{
if (Network.IsClient())
{
pm2 = (PaddleMessage2)b;
pm2.Position.Y = b.pm2.Position.Y;
pm2.Position.X = b.pm2.Position.X;
}
else
{
System.Diagnostics.Debug.WriteLine("BAD Message: " + msg);
}
}
}
I might misinterpret your problem, but I why don't you put all the thing you want to save in a class and do it like this (plus, if you use class, your data "transportation" and "management" will be much easier) :
SERIALIZATION
XmlSerializer serializer = new XmlSerializer(typeof(FrogGameData));
TextWriter textWriter = new StreamWriter("FrogGameSaveFile.xml");
serializer.Serialize(textWriter, _frogGameData);
textWriter.Close();
DESERIALIZATION
XmlSerializer deserializer = new XmlSerializer(typeof(FrogGameData));
TextReader textReader = new StreamReader("FrogGameSaveFile.xml");
_frogGameData = (FrogGameData)deserializer.Deserialize(textReader);
textReader.Close();
Note : The need-to-be-saved field should have property, because the tag in the XML will mimic the property name.
Additional Note : FrogGameData is not different than a normal class for automatic serialization like this. The XML will mimic your property order in the class for the one in the XML file.
But if you wanna need to rearrange the XML tag placement, you could do something like [XmlElement(Order = 1)],[XmlElement(Order = 2)], etc on top of your property to customize the order in XML file.
UPDATE
In case you need it, this is an example of your FrogGameData class :
public class FrogGameData
{
private Frog _frog1;
private Frog _frog2;
public Frog Frog1
{
get { return _frog1; }
set { _frog1 = value; }
}
public Frog Frog2
{
get { return _frog2; }
set { _frog2 = value; }
}
}
And the XML will pretty much like this :
<?xml version="1.0" encoding="utf-8"?>
<FrogGameData>
<Frog1>Something-depends-on-your-data</Frog1>
<Frog2>Something-depends-on-your-data</Frog2>
</FrogGameData>
But, if your class is (Note the XmlElement part) :
public class FrogGameData
{
private Frog _frog1;
private Frog _frog2;
[XmlElement(Order = 2)]
public Frog Frog1
{
get { return _frog1; }
set { _frog1 = value; }
}
[XmlElement(Order = 1)]
public Frog Frog2
{
get { return _frog2; }
set { _frog2 = value; }
}
}
Then, your XML will be :
<?xml version="1.0" encoding="utf-8"?>
<FrogGameData>
<Frog2>Something-depends-on-your-data</Frog2>
<Frog1>Something-depends-on-your-data</Frog1>
</FrogGameData>

how do I set the current class to the return types results

I have this class:
using System.IO;
using System.Xml.Serialization;
namespace ssscc.Settings
{
public class AppSettings
{
private string _companyName;
public string CompanyName
{
set { _companyName = value; }
get
{
if (string.IsNullOrWhiteSpace(_companyName))
{
LoadSettings();
}
return _companyName;
}
}
private string _companyPhone;
public string CompanyPhone
{
set
{
_companyPhone = value;
}
get
{
if (string.IsNullOrWhiteSpace(_companyPhone))
{
LoadSettings();
}
return _companyPhone;
}
}
private string GetSettingsFile()
{
var exePath = System.Windows.Forms.Application.StartupPath;
var sharedDirectory = Path.Combine(exePath, "shared");
var settingsDirectory = Path.Combine(sharedDirectory, "settings");
var settingsFile = Path.Combine(settingsDirectory, "ssscc.xml");
if (!Directory.Exists(sharedDirectory))
{
Directory.CreateDirectory(sharedDirectory);
}
if (!Directory.Exists(settingsDirectory))
{
Directory.CreateDirectory(settingsDirectory);
}
return settingsFile;
}
internal void SaveSettings(AppSettings settings)
{
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenWrite(GetSettingsFile()))
{
serializer.Serialize((Stream) stream, (object) settings);
}
}
internal void LoadSettings()
{
if (!File.Exists(GetSettingsFile()))
{
return;
}
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenRead(GetSettingsFile()))
{
var appsetting = (AppSettings) serializer.Deserialize(stream);
CompanyPhone = appsetting.CompanyPhone;
CompanyName = appsetting.CompanyName;
}
}
}
}
My question is about this code:
var appsetting = (AppSettings) serializer.Deserialize(stream);
CompanyPhone = appsetting.CompanyPhone;
CompanyName = appsetting.CompanyName;
I am pretty sure there is a way to return the appsettings directly to the class that contains the method so I do not have to loop through each property such as this:
CompanyPhone = appsetting.CompanyPhone;
CompanyName = appsetting.CompanyName;
Can I assign the properties directly without having to maintain this code?
You are getting a new instance of AppSettings while deserializing from file. You may use it, can't you? Try to replace LoadSettings with a static factory method like this:
internal static AppSettings GetInstance()
{
if (!File.Exists(GetSettingsFile()))
return null;
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenRead(GetSettingsFile()))
return (AppSettings)serializer.Deserialize(stream);
}
while to save your settings, you have no need to pass the settings object as an argument. I guess the following code should do the job:
internal void SaveSettings()
{
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenWrite(GetSettingsFile()))
serializer.Serialize((Stream)stream, this);
}
Use the factory GetInstance method to initialize settings (well, as an example):
var s = AppSettings.GetInstance();
if (s == null)
{
s = new AppSettings
{
CompanyName = "MyCompany",
CompanyPhone = "######"
};
s.SaveSettings();
}
P.S.: if properties getters and setters have no additional logic (LoadSettings method no longer exists), you could use auto-properties:
public string CompanyName { get; set; }
public string CompanyPhone { get; set; }
and GetSettingsFile may be declared as static, as it does not operate any of the instance class members:
private static string GetSettingsFile()
{
//...
return settingsFile;
}
Do you really need to have lazy-loading in here, if not, make your methods explicitly:
public class AppSettings
{
private static readonly XmlSerializer Serializer
= new XmlSerializer(typeof(AppSettings));
public string CompanyName { get; set; }
public string CompanyPhone { set; get; }
private static string GetSettingsFile()
{
return null;
}
public static void SaveSettings(AppSettings settings)
{
using (var stream = File.OpenWrite(GetSettingsFile()))
Serializer.Serialize(stream, settings);
}
internal static AppSettings LoadSettings()
{
if (!File.Exists(GetSettingsFile()))
return null;
object appsetting = null;
using (var stream = File.OpenRead(GetSettingsFile()))
appsetting = Serializer.Deserialize(stream);
return appsetting as AppSettings;
}
}
Do you can use:
var setting = AppSettings.LoadSettings();
and:
AppSettings.SaveSettings(setting);
Please note in here, creating XmlSerializer everytime will get the memory leak,
The XmlSerializer constructor will generate a pair of classes derived from XmlSerializationReader and XmlSerializationWriter by analyzing the Person class using reflection. It will create temporary C# files, compile the resulting files into a temporary assembly, and finally load that assembly into the process. Code gen like this is also relatively expensive. So the XmlSerializer caches the temporary assemblies on a per-type basis. This means that the next time an XmlSerializer for the Person class is created, the cached assembly is used rather than a new one generated.
Therefore, you should keep XmlSerializer as static.

XML deserialization generic method

I have next XML file:
<Root>
<Document>
<Id>d639a54f-baca-11e1-8067-001fd09b1dfd</Id>
<Balance>-24145</Balance>
</Document>
<Document>
<Id>e3b3b4cd-bb8e-11e1-8067-001fd09b1dfd</Id>
<Balance>0.28</Balance>
</Document>
</Root>
I deserialize it to this class:
[XmlRoot("Root", IsNullable = false)]
public class DocBalanceCollection
{
[XmlElement("Document")]
public List<DocBalanceItem> DocsBalanceItems = new List<DocBalanceItem>();
}
where DocBalanceItem is:
public class DocBalanceItem
{
[XmlElement("Id")]
public Guid DocId { get; set; }
[XmlElement("Balance")]
public decimal? BalanceAmount { get; set; }
}
Here is my deserialization method:
public DocBalanceCollection DeserializeDocBalances(string filePath)
{
var docBalanceCollection = new DocBalanceCollection();
if (File.Exists(filePath))
{
var serializer = new XmlSerializer(docBalanceCollection.GetType());
TextReader reader = new StreamReader(filePath);
docBalanceCollection = (DocBalanceCollection)serializer.Deserialize(reader);
reader.Close();
}
return docBalanceCollection;
}
All works fine but I have many XML files. Besides writing Item classes I have to write ItemCollection classes for each of them. And also I have to implement DeserializeItems method for each.
Can I deserialize my XML files without creating ItemCollection classes? And can I write single generic method to deserialize all of them?
The only solution that comes to mind - make an interface for all these classes. Any ideas?
You can deserialize a generic List<T> just fine with XmlSerializer. However, first you need to add the XmlType attribute to your DocBalanceItem so it knows how the list elements are named.
[XmlType("Document")]
public class DocBalanceItem
{
[XmlElement("Id")]
public Guid DocId { get; set; }
[XmlElement("Balance")]
public decimal? BalanceAmount { get; set; }
}
Then modify your DeserializeDocBalances() method to return a List<T> and pass the serializer an XmlRootAttribute instance to instruct it to look for Root as the root element:
public List<T> DeserializeList<T>(string filePath)
{
var itemList = new List<T>();
if (File.Exists(filePath))
{
var serializer = new XmlSerializer(typeof(List<T>), new XmlRootAttribute("Root"));
TextReader reader = new StreamReader(filePath);
itemList = (List<T>)serializer.Deserialize(reader);
reader.Close();
}
return itemList;
}
Then you should be able to do
var list = DeserializeList<DocBalanceItem>("somefile.xml");
Since the method now returns a generic List<T>, you no longer need to create custom collections for every type.
P.S. - I tested this solution locally with the provided document, it does work.
Any stringable object can be deserialized by following method.
public static T genericDeserializeSingleObjFromXML<T>(T value, string XmalfileStorageFullPath)
{
T Tvalue = default(T);
try
{
XmlSerializer deserializer = new XmlSerializer(typeof(T));
TextReader textReader = new StreamReader(XmalfileStorageFullPath);
Tvalue = (T)deserializer.Deserialize(textReader);
textReader.Close();
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("serialization Error : " + ex.Message);
}
return Tvalue;
}
In order to use this method you should already serialize the object in xml file.
Calling method is :
XmlSerialization.genericDeserializeSingleObjFromXML(new ObjectName(), "full path of the XML file");

Storing entity in XML, using MVVM to read/write in WPF Application

Say I've a class (model) called Instance with Properties DatbaseHostname, AccessManagerHostname, DatabaseUsername and DatabasePassword
public class Instance
{
private string _DatabaseHostname;
public string DatabaseHostname
{
get { return _DatabaseHostname; }
set { _DatabaseHostname = value; }
}
private string _AccessManagerHostname;
public string AccessManagerHostname
{
get { return _AccessManagerHostname; }
set { _AccessManagerHostname = value; }
}
private string _DatabaseUsername;
public string DatabaseUsername
{
get { return _DatabaseUsername; }
set { _DatabaseUsername = value; }
}
private string _DatabasePassword;
public string DatabasePassword
{
get { return _DatabasePassword; }
set { _DatabasePassword = value; }
}
}
I'm looking for a sample code to read/write this Model to XML (preferably linq2XML) => storing 1:n instances in XML.
i can manage the the view and ViewModel part myself, although it would be nice if someone had a sample of that part too..
Well, you could use Linq to XML, but your class is a perfect candidate for XML Serialization, which is much simpler IMHO :
var list = new List<Instance>();
...
// Serialization
var xs = new XmlSerializer(typeof(List<Instance>));
using (var writer = XmlWriter.Create(filename))
{
xs.Serialize(writer, list);
}
...
// Deserialization
using (var reader = XmlReader.Create(filename))
{
list = xs.Deserialize(reader) as List<Instance>;
}
Not sure how you want your xml structured, but this should work:
List<Instance> instances = new List<Instance>();
// Get your instances here...
var baseNode = new XElement("Instances");
instances.ForEach(instance => baseNode.Add("Instance",
new XAttribute("DatabaseHostname", instance.DatabaseHostname),
new XAttribute("AccessManagerHostname", instance.AccessManagerHostname),
new XAttribute("DatabaseUsername", instance.DatabaseUsername),
new XAttribute("DatabasePassword", instance.DatabasePassword)));

not able to Deserialize xml into object

I am having following peice of code ,where in i am trying to serialize and deserailize object of StringResource class.
Please note Resource1.stringXml = its coming from resource file.If i pass strelemet.outerXMl i get the object from Deserialize object ,but if i pass Resource1.stringXml i am getting following exception
{"< STRING xmlns=''> was not expected."} System.Exception {System.InvalidOperationException}
class Program
{
static void Main(string[] args)
{
StringResource str = new StringResource();
str.DELETE = "CanDelete";
str.ID= "23342";
XmlElement strelemet = SerializeObjectToXmlNode (str);
StringResource strResourceObject = DeSerializeXmlNodeToObject<StringResource>(Resource1.stringXml);
Console.ReadLine();
}
public static T DeSerializeXmlNodeToObject<T>(string objectNodeOuterXml)
{
try
{
TextReader objStringsTextReader = new StringReader(objectNodeOuterXml);
XmlSerializer stringResourceSerializer = new XmlSerializer(typeof(T),string.Empty);
return (T)stringResourceSerializer.Deserialize(objStringsTextReader);
}
catch (Exception excep)
{
return default(T);
}
}
public static XmlElement SerializeObjectToXmlNode(object obj)
{
using (MemoryStream memoryStream = new MemoryStream())
{
try
{
XmlSerializerNamespaces xmlNameSpace = new XmlSerializerNamespaces();
xmlNameSpace.Add(string.Empty, string.Empty);
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.CloseOutput = false;
writerSettings.Encoding = System.Text.Encoding.UTF8;
writerSettings.Indent = false;
writerSettings.OmitXmlDeclaration = true;
XmlWriter writer = XmlWriter.Create(memoryStream, writerSettings);
XmlSerializer xmlserializer = new XmlSerializer(obj.GetType());
xmlserializer.Serialize(writer, obj, xmlNameSpace);
writer.Close();
memoryStream.Position = 0;
XmlDocument serializeObjectDoc = new XmlDocument();
serializeObjectDoc.Load(memoryStream);
return serializeObjectDoc.DocumentElement;
}
catch (Exception excep)
{
return null;
}
}
}
}
public class StringResource
{
[XmlAttribute]
public string DELETE;
[XmlAttribute]
public string ID;
}
< STRING ID="1" DELETE="True" />
Problem is a name mismatch in your XML root node and your class
< STRING ID="1" DELETE="True" /> -- STRING here
public class StringResource -- StringResource Here.
Add xmlroot attribute to your class, and will see what happens
[XmlRoot( "STRING " )]
public class StringResource
{
[XmlAttribute]
public string DELETE;
[XmlAttribute]
public string ID;
}
Wel it's obvious Resource1.stringXml does not contain a correct XML which could be deserialized to a StringResource object. The XML should look like this:
<StringResource DELETE="CanDelete" ID="23342" />

Categories

Resources