/// <summary> /// Deserializes the specified object from a ConfigNode, restoring non-static fields and properties with /// getters and setters /// </summary> /// <param name="obj"></param> /// <param name="node"></param> /// <param name="typeFormatter"></param> /// <returns></returns> internal static bool CreateObjectFromConfigEx(this ConfigNode node, object obj, ConfigNodeTypeHandler typeFormatter = null) { bool flag = true; typeFormatter = typeFormatter ?? new ConfigNodeTypeHandler(); var fields = GetObjectFields(obj); var properties = GetObjectProperties(obj); Log.Debug("CreateObjectFromConfig: Found {0} fields and {1} properties", fields.Length, properties.Length); #region fields foreach (var field in fields) { try { System.Object[] attributes = field.GetCustomAttributes(true); if (typeof(ConfigNode).IsAssignableFrom(field.FieldType)) { Log.Debug("Field {0} is a ConfigNode", field.Name); if (node.HasNode(field.Name)) { ConfigNode target = Convert.ChangeType(field.GetValue(obj) ?? new ConfigNode(), typeof(ConfigNode)) as ConfigNode; ConfigNode subNode = node.GetNode(field.Name); // handle subsection Subsection ss = attributes.SingleOrDefault(attr => attr is Subsection) as Subsection; if (ss != null) { Log.Debug("ConfigNode field {0} under subsection {1}", field.Name, ss.Section); if (subNode.HasNode(ss.Section)) { subNode = subNode.GetNode(ss.Section); } else { Log.Warning("Field {0} tagged with Subsection {1} but no such section exists!", field.Name, ss.Section); } } else { Log.Debug("ConfigNode field {0} not under any subsection", field.Name); } // remember that subnode is the container for the node we wanted if (subNode.CountNodes == 1) { ConfigNode data = subNode.nodes[0]; // here's what we're interested in field.SetValue(obj, data); Log.Verbose("Successfully deserialized ConfigNode {0}", field.Name); } else { Log.Warning("Storage for ConfigNode {0} looks like it's missing or wrong", field.Name); } } else { Log.Warning("No serialized ConfigNode found for ConfigNode {0}", field.Name); } } else { string strValue = ReadValue(node, field.Name, field.GetCustomAttributes(true)); if (!string.IsNullOrEmpty(strValue)) { Log.Verbose("Parsing field {0}.{1} from {2}", obj.GetType().Name, field.Name, strValue ?? ""); MethodInfo mi = typeFormatter.GetType().GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.NonPublic); if (mi == null) { Log.Error("CreateObjectFromConfigEx: Failed to locate Deserialize method"); } MethodInfo deserialize = mi.MakeGenericMethod(field.FieldType); if (deserialize == null) { Log.Error("CreateObjectFromConfigEx: Failed to create generic method for field {0}", field.FieldType.Name); } if (!(bool)deserialize.Invoke(typeFormatter, new object[] { field.GetValue(obj), strValue })) { flag = false; Log.Warning("Failed to deserialize field {0}.{1} from {2}", obj.GetType().Name, field.FieldType.Name, strValue ?? ""); } else { Log.Verbose("Deserialized: {0}", field.GetValue(obj)); } } else { Log.Warning("CreateObjectFromConfigEx: Empty/null string found for field named {0}, type {1}", field.Name, field.FieldType.Name); } } } catch (Exception e) { Log.Error("Exception while deserializing field '{0}': {1}", field.Name, e); flag = false; } } #endregion #region properties foreach (var property in properties) { try { string strValue = ReadValue(node, property.Name, property.GetCustomAttributes(true)); if (!string.IsNullOrEmpty(strValue)) { Log.Verbose("Parsing property {0}.{1} from {2}", obj.GetType().Name, property.Name, strValue ?? ""); MethodInfo mi = typeFormatter.GetType().GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.NonPublic); if (mi == null) { Log.Error("CreateObjectFromConfigEx: Failed to locate Deserialize method"); } MethodInfo deserialize = mi.MakeGenericMethod(property.PropertyType); if (deserialize == null) { Log.Error("CreateObjectFromConfigEx: Failed to create generic method for property {0}", property.PropertyType.Name); } // get existing value var existing = Convert.ChangeType(property.GetGetMethod(true).Invoke(obj, null), property.PropertyType); //object[] parameters = new object[] { Activator.CreateInstance(property.PropertyType), strValue }; object[] parameters = new object[] { existing, strValue }; if (!(bool)deserialize.Invoke(typeFormatter, parameters)) { flag = false; Log.Warning("Failed to deserialize property {0}.{1} from {2}", obj.GetType().Name, property.PropertyType.Name, strValue ?? ""); } else { // use the actual setter property.SetValue(obj, parameters[0], BindingFlags.SetProperty | BindingFlags.Instance, null, null, null); } } else { Log.Warning("CreateObjectFromConfigEx: Empty/null string found for property named {0}, type {1}", property.Name, property.PropertyType.Name); } } catch (Exception e) { Log.Error("Exception while deserializing property '{0}': {1}", property.Name, e); flag = false; } } #endregion if (obj is IReeperSerializable) { ((IReeperSerializable)obj).OnDeserialize(node); } return(flag && fields.Count() > 0 || (obj is IReeperSerializable)); }
/// <summary> /// Creates a ConfigNode out of the specified object, capturing non-static fields and properties that /// have both a setter and getter /// </summary> /// <param name="obj"></param> /// <param name="typeFormatter"></param> /// <returns></returns> internal static ConfigNode CreateConfigFromObjectEx(this object obj, ConfigNodeTypeHandler typeFormatter = null) { try { ConfigNode n = new ConfigNode(obj.GetType().Name); typeFormatter = typeFormatter ?? new ConfigNodeTypeHandler(); // check if typeformatter has a serialize method for this type #region serialize fields var fields = GetObjectFields(obj); foreach (var field in fields) { System.Object[] attributes = field.GetCustomAttributes(false); var result = field.GetValue(obj); if (result != null) { // check for ConfigNodes; they're a special case if (typeof(ConfigNode).IsAssignableFrom(field.FieldType)) { Log.Debug("{0} is a ConfigNode", field.Name); // store ConfigNode in a subnode (so ConfigNode "test" would be stored in // a subnode called "test" which then contains a copy of the ConfigNode). Do // it this way so that any possible name the ConfigNode has doesn't collide // // why not rename it to field.name? we're not sure whether the node name matters // to whichever objects are storing data there and we don't want to impose any // arbitrary restrictions if we can avoid doing so var subnode = new ConfigNode(field.Name); //if (ss != null) // subnode = subnode.AddNode(ss.Section); var copy = ((ConfigNode)Convert.ChangeType(result, typeof(ConfigNode))).CreateCopy(); if (string.IsNullOrEmpty(copy.name)) { copy.name = "ConfigNode"; } Log.Debug("ConfigNode copy = {0}", copy.ToString()); Log.Debug("Original = {0}", ((ConfigNode)Convert.ChangeType(result, typeof(ConfigNode))).ToString()); subnode.ClearData(); // handle subsection attribute on ConfigNode fields Subsection ss = attributes.SingleOrDefault(attr => attr is Subsection) as Subsection; if (ss == null) { subnode.AddNode(copy); } else { subnode.AddNode(ss.Section).AddNode(copy); Log.Debug("Subnode with subsection = {0}", subnode.ToString()); } n.AddNode(subnode); } else { MethodInfo mi = typeFormatter.GetType().GetMethod("Serialize", BindingFlags.Instance | BindingFlags.NonPublic); if (mi == null) { Log.Error("CreateConfigFromObjectEx: Serialize method not found"); } MethodInfo serialize = mi.MakeGenericMethod(field.FieldType); if (serialize == null) { Log.Error("CreateConfigFromObjectEx: Failed to create generic method for {0}", field.FieldType.Name); } string serialized = serialize.Invoke(typeFormatter, new object[] { result }) as string; if (string.IsNullOrEmpty(serialized)) { Log.Warning("ConfigUtil.CreateConfigFromObjectEx: null or empty return value for serialized type {0}", field.FieldType.Name); } WriteValue(n, field.Name, serialized, attributes); } } else { Log.Warning("Could not get value for " + field.Name); } } #endregion #region serialize get/settable properties var properties = GetObjectProperties(obj); foreach (PropertyInfo property in properties) { Log.Verbose("Serializing property {0}, type {1}", property.Name, property.PropertyType.Name); var propertyValue = property.GetGetMethod(true).Invoke(obj, null); System.Object[] attributes = property.GetCustomAttributes(true); MethodInfo mi = typeFormatter.GetType().GetMethod("Serialize", BindingFlags.Instance | BindingFlags.NonPublic); if (mi == null) { Log.Error("CreateConfigFromObjectEx: Serialize method not found"); continue; } MethodInfo serialize = mi.MakeGenericMethod(property.PropertyType); if (serialize == null) { Log.Error("CreateConfigFromObjectEx: Failed to create generic method for {0}", property.PropertyType.Name); } string serialized = serialize.Invoke(typeFormatter, new object[] { propertyValue }) as string; if (string.IsNullOrEmpty(serialized)) { Log.Warning("ConfigUtil.CreateConfigFromObjectEx: null or empty return value for serialized type {0}", property.PropertyType.Name); } WriteValue(n, property.Name, serialized, attributes); } #endregion if (obj is IReeperSerializable) { ((IReeperSerializable)obj).OnSerialize(n); } return(n); } catch (Exception e) { Log.Error("ConfigUtil.CreateConfigFromObjectEx: Exception {0}", e); return(null); } }