Before I continue, I'm going to begin by saying I'm not by any means
an expert- I've been using .NET with C# for about 4 months now, and
basically just learning by example and docs.
A game project I work on uses Xml serialization to store game objects
for loading. One of the techniques we use is to make an array
property and use the set block to perform actions on the values after
they're loaded from the xml, as in:
public string[] Ids
{
set
{
foreach(string id in value) this.AddId(id);
}
get { return null}
}
Well, I had set about wanting some type independance and thought I
would use an ArrayList for a different such task. But when I set
about making a similar property of type ArrayList, I found that it
wasn't working at all. Set would get called, but the value was always
empty. (value.Count==0).
There IS a document on MSDN that describes the serialization of
ArrayLists. At first, therefore, I believed I was using the wrong
attributes and that the xml reader was ignoring what it thought was
unused elements.
http://msdn.microsoft.com/library/de...attributes.asp
While MSDN says that to serialize an ArrayList you should use the
[XmlElement] attribute, this is misleading in the document because it
creates a flat array- no <ArrayListName>(contents)</ArrayListName>,
just a sequence of named elements.
Instead, use the attributes that are also used for arrays.
[XmlArray("NameOfArrayElement")] and one or more
[XmlArrayItem("NameOfType", typeof(YourType)]. The document describes
these, and they work just fine for ArrayLists too.
I googled for MANY hours reading everything I could find on the
XmlSerializer and properties and arraylists-- so far I've not found
anywhere that lists what I found.
For Arrays, the XmlSerializer creates the array, fills it internally,
and then calls set() on the property with the filled array.
For ArrayLists, it uses a much more convoluted method:
1) The serializer calls get() to find out if the ArrayList is null or
not.
2) If and ONLY IF it is null, it then calls set() with a new, empty
ArrayList. This is the only time set() EVER MIGHT be called.
3) For every object deserialized from the xml, the serializer calls
get()- expecting a real, persisted arraylist to be returned- and uses
Add() on the result to add the object. If your get() returns a Clone
of the arraylist, it will never actually fill.
What does this mean? You CANNOT use a property to accept input from
an xml file without saving the result- the ArrayList MUST exist in a
field somewhere and MUST be returned by the get() property and setable
by the set() property.
Also, set() is NEVER called on the completely deserialized ArrayList-
only on empty new ones, if necessary. Therefore, you must use the
IDeserializationCallback interface and implement OnDeserialization()
to act on the contents of the filled arraylist after loading.
Having figured all this out, I THEN ran into a hurdle for one very
infrequently mentioned fact: IDeserializationCallback applies only to
the BinaryFormatter, NOT the XmlSerializer. Therefore, in your
applications loading method, you may find it to your advantage to
define your own IXmlDeserializationCallback interface and check for
it when an object is loaded.
The disadvantage to this method is that because the XmlSerializer is
not doing the actual calling, internal objects (fields, properties)
which implement this interface will NOT have the method invoked.
Therefore, in your implementation of OnXmlDeserialization(), you
should call ONXmlDeserialization for your fields that implement
IXmlDeserializationCallback.
I hope this helps somebody- this is my first post writing like I know
what I'm talking about! ;) I'll feel like quite the fool if everyone
already knew about this and I was the only one in the dark...)
--------------------------------
From: Jamus Sprinson
jamuspsi at earthlink dot net