XML Serializer not working on a property - c#

In the code that I'm working on, there are some properties that are serialized. All of them works fine as far as I can see, but one. Here's the code:
// Fields that the properties use.
private bool _useAlertColors = false;
private List<Int32> _colorArgb = new List<Int32>(new Int32[]{Color.White.ToArgb(), Color.Yellow.ToArgb(), Color.Red.ToArgb()});
// The one that does not work.
public List<Color> AlertColors
{
get
{
List<Color> alertColors = new List<Color>();
foreach (Int32 colorValue in _colorArgb)
{
alertColors.Add(Color.FromArgb(colorValue));
}
return alertColors;
}
set
{
// If there's any difference in colors
// then add the new color
for (int i = 0; i < _colorArgb.Count; i++)
{
if (_colorArgb[i] != value[i].ToArgb())
{
_colorArgb[i] = value[i].ToArgb();
HasChanged = true;
}
}
}
}
// One of those that work.
public bool UseAlertColors
{
get
{
return _useAlertColors;
}
set
{
if (_useAlertColors != value)
{
_useAlertColors = value;
HasChanged = true;
}
}
}
// Serializing method.
public bool Serialize(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
Logger.Log("Can't save settings, empty or null file path.");
return false;
}
FileStream fileStream = null;
try
{
fileStream = new FileStream(filePath, FileMode.Create);
FilePath = filePath;
System.Xml.Serialization.XmlSerializerFactory xmlSerializerFactory =
new XmlSerializerFactory();
System.Xml.Serialization.XmlSerializer xmlSerializer =
xmlSerializerFactory.CreateSerializer(typeof(Settings));
xmlSerializer.Serialize(fileStream, this);
Logger.Log("Settings have been saved successfully to the file " + filePath);
}
catch (ArgumentException argumentException)
{
Logger.Log("Error while saving the settings. " + argumentException.Message);
return false;
}
catch (IOException iOException)
{
Logger.Log("Error while saving the settings. " + iOException.Message);
return false;
}
finally
{
if (fileStream != null)
fileStream.Close();
}
return true;
}
After the serialization, this is what I end up with:
<UseAlertColors>true</UseAlertColors>
<AlertColors>
<Color />
<Color />
<Color />
</AlertColors>
What is wrong with the AlertColors property? Why is it not serialized?
Edit: I've changed the AlertColors property accordingly and it still is not working:
public List<int> AlertColors
{
get
{
return _colorArgb;
}
set
{
// If there's any difference in colors
// then add the new color
for (int i = 0; i < _colorArgb.Count; i++)
{
if (_colorArgb[i] != value[i])
{
_colorArgb[i] = value[i];
HasChanged = true;
}
}
}
}
What I get in the .xml file is this:
<AlertColors>
<int>-1</int>
<int>-8355840</int>
<int>-65536</int>
</AlertColors>
Which are the very first values that were set when the _colorArgb field was initialized. I can see the field change during the program execution, but when the covering property is serialized, it is serialized with the initial value of the underlying field.
What is causing the problem?

XMLSerializer only uses public properties of a class when serializing - The Color Class has none.
Heres a good example of how to get around it: http://www.codeproject.com/Articles/2309/NET-XML-Serialization-a-settings-class

Related

Using reflection to load values from XML into a structure

I'm using Reflection to load some values from an XML into a structure. All works but I got a problem to manage the array. With an Int32 it's ok, but how can I manage an Int32[]?
EDIT: Here's my code ... it's not complete due the lenght ... I want to improve the save and load for the array types.
//My struct ... it should be a class too
public struct stOptions
{
public int IntNumber;
public double DoubleNumber;
public string String;
public Point aPoint;
}
//How my class works
private void Form1_Load(object sender, EventArgs e)
{
stOptions options = new stOptions();
//Populate the struct
options.aPoint = new Point(12, 24);
options.DoubleNumber = 34d;
options.IntNumber = 17;
options.String = "Hello";
ManageSettings ms = new ManageSettings();
ms.SaveSettings("e:\\test001.xml", options);
options = default(stOptions);
options = ms.LoadSettings<stOptions>("e:\\test001.xml");
}
//A portion of my class
public T LoadSettings<T>(string FileName)
{
Type type = typeof(T);
var returnObject = Activator.CreateInstance(type);
List<Settings> list = null;
try
{
using (StreamReader reader = File.OpenText(FileName))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<Settings>));
list = (List<Settings>)serializer.Deserialize(reader);
}
}
catch
{
//Error accessing the file
_errors.Add("Unable to locate the file or access denied: " + FileName);
return default(T);
}
try
{
foreach (Settings entry in list)
{
FieldInfo field = returnObject.GetType().GetField(entry.Key.ToString());
if (field != null)
{
SetField(field, entry.Value.ToString(), returnObject);
}
field = null;
PropertyInfo prop = returnObject.GetType().GetProperty(entry.Key.ToString());
if (prop != null)
{
SetField(prop, entry.Value.ToString(), returnObject);
}
prop = null;
}
list = null;
}
catch
{
//Error processing the XML
_errors.Add("Errore processing the XML file: " + FileName);
return default(T);
}
return (T)returnObject;
}
private void SetField(FieldInfo prop, string value, object returnObject)
{
switch (prop.FieldType.Name.ToLower())
{
case "uint16":
prop.SetValue(returnObject, Convert.ToUInt16(value));
break;
case "uint32":
prop.SetValue(returnObject, Convert.ToUInt32(value));
break;
etc.
[...]
default:
//An unmanaged type
Debug.WriteLine("Found an unmanaged type: " + prop.FieldType.Name);
break;
}
}
When it is completed I will publish it.

Cant serialize a list of objects (each object contain a list) to xml

Im trying to understant how serialize works, but I have been stuck on a problem a while now.
What im trying to do is that I have a manager that handle a list of object. In this case recipe´s. In each recipe there is a list of ingredients. My manager is derived from a Listmanager class. The list of ingredients also uses the List manager class.
I have used this when I serialized a similar manager, except I serialized to binary (.dat). That worked and even worked with deserialize. I know xml is a bit different, but still worth mentioning..
The problem --> All I get is this when I serialize:
<?xml version="1.0"?>
<RecipeManager xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
I would like the xml-document to resemble something like this:
<Recipe>
<Name>Food<Name>
<Ingredient>blabla<Ingredient>
<Ingredient>blabla2<Ingredient>
<Ingredient>blabla3<Ingredient>
</Recipe>
This is my Listmanager, it has a bunch of other methods but this just so you can get an idea of how it looks:
[Serializable]
public class ListManager<T> : IListManager<T>
{
private List<T> list;
public ListManager()
{
list = new List<T>();
}
public int Count { get { return list.Count; } }
public void Add(T aType)
{
list.Add(aType);
}
public bool ChangeAt(T aType, int index)
{
if (CheckIndex(index))
{
list[index] = aType;
return true;
}
else
{
return false;
}
}
/// <summary>
/// Tittar så det finns ett objekt på platsen som användaren valt
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public bool CheckIndex(int index)
{
if (index < 0)
{
return false;
}
else if (list[index] != null)
{
return true;
}
else
return false;
}
This is my RecipeManager class:
[Serializable]
public class RecipeManager : ListManager<Recipe>
{
/// <summary>
/// Konstruktor
/// </summary>
public RecipeManager()
{
TestData();
}
private void TestData()
{
Recipe testInfo = new Recipe();
testInfo.Name = "AnimalSuperFood";
testInfo.IngredientsList.Add("2dl of water");
testInfo.IngredientsList.Add("3g of meat");
testInfo.IngredientsList.Add("1ml of lemon");
Add(testInfo);
}
}
This is my recipe class:
[Serializable]
[XmlRoot("Recipe")]
public class Recipe
{
#region props
private ListManager<string> ingredientsList;
[XmlArray("Ingredient")]
public ListManager<string> IngredientsList
{
get { return ingredientsList; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
#endregion
/// <summary>
/// Konstruktor
/// </summary>
public Recipe()
{
ingredientsList = new ListManager<string>();
}
public override string ToString()
{
string strOut = string.Format("{0}: ", Name); ;
for (int i = 0; i < IngredientsList.Count; i++)
{
if (i != (IngredientsList.Count - 1))
{
strOut += string.Format("{0} mixed with ", IngredientsList.GetAt(i));
}
else
{
strOut += string.Format("{0}", IngredientsList.GetAt(i));
}
}
return strOut;
}
}
This is where i send my manager that contains the objects:
using (SaveFileDialog dlg = new SaveFileDialog())
{
dlg.Title = "Save xml file";
dlg.Filter = "xml file (*.xml)|*.xml";
if (dlg.ShowDialog() == DialogResult.OK)
{
try
{
SerializationUtility.XmlFileSerialize(recipeMgr, dlg.FileName);
MessageBox.Show("Save succesful", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
And last this is where i serialize:
public static void XmlFileSerialize<T>(T obj, string filePath)
{
using (Stream s = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
XmlSerializer xmlWriter = new XmlSerializer(typeof(T));
xmlWriter.Serialize(s, obj);
}
}
And last but not least, please explain why my question was bad if you Think so. dont just vote down and leave.. I am trying to learn.. thanks!
Ok, found your problem after re-reading the code: Any property you want to be serialized must be public.
Your "list" on ListManager is private, so it will not be serialized and there will be nothing.
Make it public and it will work.
Also any other private property/field you want to serialize must be public.

How can I clear/prevent binding to datagrid if duplicate exists?

Here's my property that determines if I should bind the other properties or not:
public string LotNumber {
get {
return lotNumber;
}
set {
using (var db = new DDataContext()) {
lotNumber = value;
// Check for duplicates
bool isDuplicate =
db.LotInformation.Any(r => r.lot_number == lotNumber);
if (isDuplicate == true) {
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
lotNumber = null;
// Inform user that the lot_number already exists
errorWindow.Message =
LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(
LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(
LanguageResources.Resource.Lot_Exists_Already);
return;
} else {
lotNumber = value;
}
RaisePropertyChanged("LotNumber");
}
}
}
My problem right now is if I upload a file and if the lot number already exists in the database, the boolean returns true and an error message is thrown. However, after that,it loops again and then the boolean is set to false since now the value is null and it still binds the data afterward. How can I break out of the loop and just make it stop running/clear/prevent binding when bool is true in the case above?
I assume you have some code like this:
LotNumber = "ABC5"; // ABC5 already exists in the database - uh oh!
And then you're trying to figure everything out in the "setter". It's already too late by that point. Instead, move your logic into separate methods:
private bool LotNumberExists(string lotNumber)
{
using (var db = new DDataContext())
{
return db.LotInformation.Any(r => r.lot_number == lotNumber);
}
}
private void ClearFields()
{
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
}
private void InformUserOfDuplicate()
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
}
Then check the return value of that method before setting LotNumber.
private void SomeOtherMethod()
{
string someLotNumber = "ABC5";
if (LotNumberExists(someLotNumber)
{
ClearFields();
InformUserOfDuplicate();
return;
}
LotNumber = someLotNumber;
}
Turn your setter back into a simple setter without a ton of logic wrapped up in it:
public string LotNumber
{
get { return lotNumber; }
set
{
lotNumber = value;
RaisePropertyChanged("LotNumber");
}
}

Set default value for attributes which are null while deserializing XML

I have a xml file which I am trying to deserialize.
I am implementing IXmlSerializable interface for Serialization and Deserialization.
I don't see any problem with the Serialization.
But with the deserialization I have few problems.
1). I am using xml reader's 'ReadToFollowing' for reading elements. With this approach I have to read the values in the order of elements. If there is any mismatch in the order I am not able to read the remainig values.
2). As per my requirement I have to provide backward compatibility in deserializing the xml. Because of this when I load some of the old version xml files(which may not contain all the elements as the latest version xml), 'ReadToFollowing' throwing exceptions.
Latest version Xml
<LEFTSECTION>
<DATA />
<FONTNAME>Arial</FONTNAME>
<FONTSTYLE>Regular</FONTSTYLE>
<FONTSIZE>10</FONTSIZE>
<TEXTCOLOR>-16777216</TEXTCOLOR>
<STRIKEOUT>0</STRIKEOUT>
<UNDERLINE>0</UNDERLINE>
<BORDER>0</BORDER>
<IMAGE>0</IMAGE>
<IMAGENAME />
<ALIGNMENT>4</ALIGNMENT>
<SECTIONHEIGHT>0.5454546</SECTIONHEIGHT>
<SECTIONWIDTH>0.33</SECTIONWIDTH>
</LEFTSECTION>
Old version Xml
<LEFTSECTION>
<DATA>asas#APP_OEM_NAME#</DATA>
<FONTNAME>Arial Unicode MS</FONTNAME>
<FONTSTYLE>Regular</FONTSTYLE>
<FONTSIZE>10</FONTSIZE>
<TEXTCOLOR>-16777216</TEXTCOLOR>
<STRIKEOUT>0</STRIKEOUT>
<UNDERLINE>0</UNDERLINE>
<BORDER>0</BORDER>
<IMAGE>0</IMAGE>
<IMAGENAME>
</IMAGENAME>
</LEFTSECTION>
Please help me on this.
Okay. I've had a play and this is what I've come up with:
Serializeable class:
[Serializable]
[XmlRoot("LEFTSECTION")]
public class NewClass
{
[XmlElement("DATA")]
[System.ComponentModel.DefaultValueAttribute("")]
public string Data;
[XmlElement("FONTNAME")]
public string FontName;
[XmlElement("FONTSTYLE")]
public string FontStyle;
[XmlElement("FONTSIZE")]
public int FontSize;
[XmlElement("TEXTCOLOR")]
public int TextColor;
[XmlElement("STRIKEOUT")]
public int Strikeout;
[XmlElement("UNDERLINE")]
public int Underline;
[XmlElement("BORDER")]
public int Border;
[XmlElement("IMAGE")]
public int Image;
[XmlElement("IMAGENAME")]
public string ImageName;
[System.ComponentModel.DefaultValue(0)]
[XmlElement("ALIGNMENT")]
public int Alignment;
[XmlElement("SECTIONHEIGHT")]
public double SectionHeight;
[XmlElement("SECTIONWIDTH")]
public double SectionWidth;
}
Then code in my test program:
NewClass test = new NewClass();
XmlSerializer serializer = new XmlSerializer(typeof(NewClass));
FileStream file = new FileStream("input.xml", FileMode.Open);
test = (NewClass) serializer.Deserialize(file);
file.Close();
file = new FileStream("old.xml", FileMode.Open);
test = (NewClass)serializer.Deserialize(file);
file.Close();
Contents of input.xml:
<?xml version="1.0" encoding="utf-8" ?>
<LEFTSECTION>
<DATA />
<FONTNAME>Arial</FONTNAME>
<FONTSTYLE>Regular</FONTSTYLE>
<FONTSIZE>10</FONTSIZE>
<TEXTCOLOR>-16777216</TEXTCOLOR>
<STRIKEOUT>0</STRIKEOUT>
<UNDERLINE>0</UNDERLINE>
<BORDER>0</BORDER>
<IMAGE>0</IMAGE>
<IMAGENAME />
<ALIGNMENT>4</ALIGNMENT>
<SECTIONHEIGHT>0.5454546</SECTIONHEIGHT>
<SECTIONWIDTH>0.33</SECTIONWIDTH>
</LEFTSECTION>
Contents of old.xml:
<LEFTSECTION>
<DATA>asas#APP_OEM_NAME#</DATA>
<FONTNAME>Arial Unicode MS</FONTNAME>
<FONTSTYLE>Regular</FONTSTYLE>
<FONTSIZE>10</FONTSIZE>
<TEXTCOLOR>-16777216</TEXTCOLOR>
<STRIKEOUT>0</STRIKEOUT>
<UNDERLINE>0</UNDERLINE>
<BORDER>0</BORDER>
<IMAGE>0</IMAGE>
<IMAGENAME>
</IMAGENAME>
</LEFTSECTION>
This populates my class correctly. Notice that on Data and Alignment properties of my class I set default values if they don't exist. Also they're all renamed from what is in the file.
I hope that this helps.
edit
Ah I see, you're stuck with IXmlSerializable methods for your classes.
Try this it's not pretty but it appears to work:
Here's my IXMLSerializable class:
public class XmlSerializableNewClass : IXmlSerializable
{
public string Data;
public string FontName;
public string FontStyle;
public int FontSize;
public int TextColor;
public int Strikeout;
public int Underline;
public int Border;
public int Image;
public string ImageName;
public int Alignment;
public double SectionHeight;
public double SectionWidth;
public string[]elementNames={"DATA", "FONTNAME", "FONTSTYLE","FONTSIZE","TEXTCOLOR","STRIKEOUT", "UNDERLINE", "BORDER", "IMAGE", "IMAGENAME", "ALIGNMENT", "SECTIONHEIGHT", "SECTIONWIDTH"};
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
//set default values
Data=string.Empty;
FontName = string.Empty;
FontStyle = string.Empty;
FontSize = 0;
TextColor = 0;
Strikeout = 0;
Underline = 0;
Border = 0;
Image = 0;
ImageName = string.Empty;
Alignment = 0;
SectionHeight = 0.0;
SectionWidth = 0.0;
reader.MoveToContent();
Boolean isEmptyElement= false;
isEmptyElement = reader.IsEmptyElement;
reader.ReadStartElement();
for (int i=0; i< elementNames.Length; i++)
{
isEmptyElement = reader.IsEmptyElement;
string s = reader.Name;
switch (s)
{
case "DATA":
if (!isEmptyElement)
{
Data = reader.ReadElementString("DATA");
}
else
{
Data = string.Empty;
reader.ReadStartElement();
}
break;
case "FONTNAME":
if (!isEmptyElement)
{
FontName = reader.ReadElementString("FONTNAME");
}
else
{
FontName = string.Empty;
reader.ReadStartElement();
}
break;
case "FONTSTYLE":
if (!isEmptyElement)
{
FontStyle = reader.ReadElementString("FONTSTYLE");
}
else
{
FontStyle = string.Empty;
reader.ReadStartElement();
}
break;
case "FONTSIZE":
if (!isEmptyElement)
{
FontSize = reader.ReadElementContentAsInt();
}
else
{
FontSize = 0;
reader.ReadEndElement();
}
break;
case "TEXTCOLOR":
if (!isEmptyElement)
{
TextColor = reader.ReadElementContentAsInt();
}
else
{
TextColor = 0;
reader.ReadStartElement();
}
break;
case "STRIKEOUT":
if (!isEmptyElement)
{
Strikeout = reader.ReadElementContentAsInt();
}
else
{
Strikeout = 0;
reader.ReadStartElement();
}
break;
case "UNDERLINE":
if (!isEmptyElement)
{
Underline = reader.ReadElementContentAsInt();
}
else
{
Underline = 0;
reader.ReadStartElement();
}
break;
case "BORDER":
if (!isEmptyElement)
{
Border = reader.ReadElementContentAsInt();
}
else
{
Border = 0;
reader.ReadStartElement();
}
break;
case "IMAGE":
if (!isEmptyElement)
{
Image = reader.ReadElementContentAsInt();
}
else
{
Image = 0;
reader.ReadStartElement();
}
break;
case "IMAGENAME":
if (!isEmptyElement)
{
ImageName = reader.ReadElementString("IMAGENAME");
}
else
{
ImageName = string.Empty;
reader.ReadStartElement();
}
break;
case "ALIGNMENT":
if (!isEmptyElement)
{
Alignment = reader.ReadElementContentAsInt();
}
else
{
Alignment = 0;
reader.ReadStartElement();
}
break;
case "SECTIONHEIGHT":
if (!isEmptyElement)
{
SectionHeight = reader.ReadElementContentAsDouble();
}
else
{
SectionHeight = 0;
reader.ReadStartElement();
}
break;
case "SECTIONWIDTH":
if (!isEmptyElement)
{
SectionWidth = reader.ReadElementContentAsDouble();
}
else
{
SectionWidth = 0;
reader.ReadEndElement();
}
break;
}
}
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
throw new NotImplementedException();
}
#endregion
}
I've gone a bit overboard with handling empty elements.
Here's the calling code, everything else is the same:
XmlSerializableNewClass test2 = new XmlSerializableNewClass();
System.Xml.XmlReaderSettings settings = new System.Xml.XmlReaderSettings();
settings.ConformanceLevel = System.Xml.ConformanceLevel.Fragment;
settings.IgnoreWhitespace = true;
settings.IgnoreComments = true;
System.Xml.XmlReader reader = System.Xml.XmlReader.Create("input.xml", settings);
test2.ReadXml(reader);
reader = System.Xml.XmlReader.Create("old.xml", settings);
test2.ReadXml(reader);

Several strategies for serialization

I have a class, which can be serialized using binary formatter. I want to have a two strategies for serializing. The second strategy should be different from the main one by excluding some specific fields from being serialized. How can I achieve this?
You can use NonSerializedAttribute on that field which you don't want to Serialize.
Also look at this MSDN article on this.
Create two memento classes each with the information you want to serialize, and add code to read in/out the values from the main class to the memento classes.
link to Memento strategy in Wikipedia.
link to dotfactory article on the strategy with C# and VB.NET
Example
Crate a class with two dependent properties, "number" and "square". Setting each one completely defines the class. Create two seriazation classes to serialize the main class two different ways:
class Data // The main class to store data in
{
int x;
public Data() { this.x = 0; }
public int Number
{
get { return x; }
set { x = value; }
}
public int Square
{
get { return x * x; }
set { x = (int)Math.Sqrt(value); }
}
public void FromNumberStore(NumberMemento mem)
{
this.Number = mem.Number;
}
public void FromSqureStore(SquareMemento mem)
{
this.Square = mem.Square;
}
}
[Serializable]
class NumberMemento // memento #1
{
int x;
public NumberMemento() { x = 0; }
public NumberMemento(Data data)
{
this.x = data.Number;
}
public int Number
{
get { return x; }
set { x = value; }
}
}
[Serializable]
class SquareMemento // memento #2
{
int x2;
public SquareMemento() { x2 = 0; }
public SquareMemento(Data data)
{
this.x2 = data.Square;
}
public int Square
{
get { return x2; }
set { x2 = value; }
}
}
class Program // Sample code to check all around serialization.
{
static void Main(string[] args)
{
Data store = new Data();
store.Number = 9;
{
// Write and read based on number
NumberMemento mem1 = new NumberMemento(store);
BinaryFormatter bf1 = new BinaryFormatter();
FileStream fs = new FileStream("numstore.dat", FileMode.Create);
bf1.Serialize(fs, mem1);
fs.Close();
// clear data and deserialize
store.Number = 0;
fs = new FileStream("numstore.dat", FileMode.Open);
mem1 = bf1.Deserialize(fs) as NumberMemento;
fs.Close();
store.FromNumberStore(mem1);
// check store.Number == 9
}
{
// Write and read based on square
SquareMemento mem2 = new SquareMemento(store);
BinaryFormatter bf2 = new BinaryFormatter();
FileStream fs = new FileStream("sqrstore.dat", FileMode.Create);
bf2.Serialize(fs, mem2);
fs.Close();
// clear data and deserialize
store.Number = 0;
fs = new FileStream("sqrstore.dat", FileMode.Open);
mem2 = bf2.Deserialize(fs) as SquareMemento;
fs.Close();
store.FromSqureStore(mem2);
// check store.Number == 9
}
}
}

Categories

Resources