public static void DeserializeFromXML(object obj, XmlNode node)
{
XmlNode mainNode = node;
if (node.Name != obj.GetType().Name)
{
mainNode = node.SelectSingleNode(obj.GetType().Name);
if (mainNode == null)
{
throw new InvalidDataException("Node not found in XML: " + obj.GetType().Name);
}
}
DeserializeIgnoreAttribute[] ignoreAttributes =
(DeserializeIgnoreAttribute[])obj.GetType().GetCustomAttributes(typeof(DeserializeIgnoreAttribute), true);
foreach (XmlNode child in mainNode.ChildNodes)
{
string elementValue = child.InnerText;
PropertyInfo prop = obj.GetType().GetProperty(child.Name);
if (prop == null)
{
if (ignoreAttributes.Length == 0 ||
!Array.Exists(ignoreAttributes, DeserializeIgnoreAttribute.MatchesPropertyName(child.Name)))
{
throw new InvalidDataException("The property '" + child.Name + "' could not be read. This game may require a newer version of AGS.");
}
continue;
}
// Process any existing value conversions; this helps to upgrade game from older version
DeserializeConvertValueAttribute[] conversions =
(DeserializeConvertValueAttribute[])prop.PropertyType.GetCustomAttributes(typeof(DeserializeConvertValueAttribute), true);
if (conversions.Length > 0)
{
foreach (DeserializeConvertValueAttribute conversion in conversions)
{
elementValue = conversion.Convert(elementValue);
}
}
if (!prop.CanWrite)
{
// do nothing, read-only
}
else if (child.Attributes.GetNamedItem("IsNull") != null)
{
prop.SetValue(obj, null, null);
}
else if (prop.PropertyType == typeof(Boolean))
{
prop.SetValue(obj, Convert.ToBoolean(elementValue), null);
}
else if (prop.PropertyType == typeof(int))
{
prop.SetValue(obj, Convert.ToInt32(elementValue), null);
}
else if (prop.PropertyType == typeof(short))
{
prop.SetValue(obj, Convert.ToInt16(elementValue), null);
}
else if (prop.PropertyType == typeof(string))
{
prop.SetValue(obj, elementValue, null);
}
else if (prop.PropertyType == typeof(DateTime))
{
// Must use CultureInfo.InvariantCulture otherwise DateTime.Parse
// crashes if the system regional settings short date format has
// spaces in it (.NET bug)
DateTime dateTime = DateTime.MinValue;
if(DateTime.TryParseExact(elementValue, "u", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
{
// Get and set audio files time stamps
prop.SetValue(obj, dateTime, null);
}
else
{
// Release Date timestamp doesn't store time of the day, and as such it ends up in the else-statement
prop.SetValue(obj, DateTime.Parse(elementValue, CultureInfo.InvariantCulture), null);
}
}
else if (prop.PropertyType.IsEnum)
{
prop.SetValue(obj, Enum.Parse(prop.PropertyType, elementValue), null);
}
else if (prop.PropertyType.IsClass)
{
ConstructorInfo constructor = prop.PropertyType.GetConstructor(new Type[] { typeof(XmlNode) });
prop.SetValue(obj, constructor.Invoke(new object[] { child }), null);
}
// For compatibility with various Custom Resolution beta builds
// TODO: find a generic solution for doing a conversions like this without
// using hard-coded property name (some serialization attribute perhaps)
else if (prop.PropertyType == typeof(Size) && prop.Name == "CustomResolution")
{
prop.SetValue(obj, CompatStringToResolution(elementValue), null);
}
else
{
throw new InvalidDataException("Unknown data type: " + prop.PropertyType.Name);
}
}
}