I'm trying to deserialize some settings from an xml file. The problematic property/underlying field is one called AlertColors
. I initialize the underlying field to white, yellow, and red to make sure that a new instance of this class has a valid color setting. But when I deserialize, _colorArgb
ends up with six values, first three are the initialization values and the last three are the ones that are read from the xml file. But the property AlertColors
do not append to the field, but changes its elements. Why do I end up with a field with six colors?
Here's the code:
private List<int> _colorArgb = new List<int>(new int[] { Color.White.ToArgb(), Color.Yellow.ToArgb(), Color.Red.ToArgb() });
public List<int> AlertColors
{
get
{
return _colorArgb;
}
set
{
for (int i = 0; i < Math.Min(_colorArgb.Count, value.Count); i++)
{
if (_colorArgb[i] != value[i])
{
HasChanged = true;
}
}
_colorArgb = value;
}
}
public bool Deserialize(string filePath)
{
if (!File.Exists(filePath))
{
Logger.Log("Error while loading the settings. File does not exist.");
return false;
}
FileStream fileStream = null;
try
{
fileStream = new FileStream(filePath, FileMode.Open);
System.Xml.Serialization.XmlSerializerFactory xmlSerializerFactory =
new XmlSerializerFactory();
System.Xml.Serialization.XmlSerializer xmlSerializer =
xmlSerializerFactory.CreateSerializer(typeof(Settings));
Settings deserializedSettings = (Settings)xmlSerializer.Deserialize(fileStream);
GetSettings(deserializedSettings);
Logger.Log("Settings have been loaded successfully from the file " + filePath);
}
catch (IOException iOException)
{
Logger.Log("Error while loading the settings. " + iOException.Message);
return false;
}
catch (ArgumentException argumentException)
{
Logger.Log("Error while loading the settings. " + argumentException.Message);
return false;
}
catch (InvalidOperationException invalidOperationException)
{
Logger.Log("Error while loading the settings. Settings file is not supported." +
invalidOperationException.Message);
return false;
}
finally
{
if (fileStream != null)
fileStream.Close();
FilePath = filePath;
}
return true;
}
protected void GetSettings(Settings settings)
{
AlertColors = settings.AlertColors;
}
And the relevant part of the xml file that I'm deserializing:
<AlertColors>
<int>-1</int>
<int>-15</int>
<int>-65536</int>
</AlertColors>
Basically, that's just how XmlSerializer
works. Unless the list is null
, it never expects to try and set a value. In particular, most of the time, sub-item lists don't have a setter - they are things like:
private readonly List<Child> children = new List<Child>();
public List<Child> Children { get { return children; } }
(because most people don't want external callers to reassign the list; they just want them to change the contents).
Because of this, XmlSerializer
operates basically like (over-simplifying):
var list = yourObj.SomeList;
foreach({suitable child found in the data})
list.Add({new item});
One fix is to use an array rather than a list; it always expects to assign an array back to the object, so for an array it is implemented more like (over-simplifying):
var list = new List<SomeType>();
foreach({suitable child found in the data})
list.Add({new item});
yourObj.SomeList = list.ToArray();
However, for a fixed number of values, a simpler implementation might be just:
public Foo Value1 {get;set;}
public Foo Value2 {get;set;}
public Foo Value3 {get;set;}
(if you see what I mean)
To get your desired result without changing your data types, you could use a DataContractSerializer (using System.Runtime.Serialization;) instead of the normal XmlSerializer. It doesn't call default constructors therefore you will end up with 3 colours instead of 6.
var ser = new DataContractSerializer(typeof(Settings));
var reader = new FileStream(@"c:\SettingsFile.xml", FileMode.Open);
var deserializedSettings = (Settings)ser.ReadObject(reader);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With