/// <summary> /// Loads/serializes an xml file into a settings class based on class type /// </summary> /// <param name="xmlfile">The path to the file</param> /// <param name="SettingsClass">The type of the settings class to load into</param> /// <param name="propertiesToExclude">A string list of properties (in the class) to not look for</param> /// <param name="classInstance">The actual object to append the xml settings to</param> /// <returns></returns> public static bool LoadSettings(string xmlfile, Type SettingsClass, string[] propertiesToExclude, object classInstance) { //first check if the file even exists if (!File.Exists(xmlfile)) { Logging.Warning("Xml settings file {0} does not exist, using defaults set in class{1}{2}", xmlfile, SettingsClass.GetType().ToString(), Environment.NewLine); return(false); } //get all fields from the class FieldInfo[] fields = SettingsClass.GetFields(); //get all types from the types in the class List <Type> typesOfTypesInClass = new List <Type>(); foreach (FieldInfo fieldInClass in fields) { //https://stackoverflow.com/questions/5090224/reflection-get-type-of-fieldinfo-object Type t = fieldInClass.FieldType; if (!typesOfTypesInClass.Contains(t)) { typesOfTypesInClass.Add(t); } } //now we have a list of all "types" that exist in the class //parse the xml list XmlDocument doc = new XmlDocument(); try { doc.Load(xmlfile); } catch (XmlException ex) { Logging.Error("Failed to load {0}, using defaults set in class{1}{2}{3}", xmlfile, SettingsClass.GetType().ToString(), Environment.NewLine, ex.ToString()); return(false); } //using child of child rather than xpath gets around the fact that the root element name has changed or can change XmlNodeList settings = doc.ChildNodes[0].ChildNodes; //legacy compatibility: if it's modpackSettings, there's some V1 bad names that need to be manually parsed if (SettingsClass.Equals(typeof(ModpackSettings))) { ModpackSettings.ApplyOldSettings(settings); } for (int i = 0; i < settings.Count; i++) { //verify that the setting name in xml matches a fieldInfo property in the class FieldInfo[] matches = fields.Where(f => f.Name.Equals(settings[i].Name)).ToArray(); //Logging.WriteToLog(string.Empty + matches.Count() + " matches for xml setting name " + settings[i].Name, Logfiles.Application, LogLevel.Debug); if (matches.Count() > 1) { throw new BadMemeException("ugh"); } else if (matches.Count() == 0) { Logging.Warning("no match for xml setting {0}", settings[i].Name); } else { FieldInfo settingField = matches[0]; //we have, based on name, matched the xml property to a property in the class //now set the value //BUT also check to make sure the item is not on the blacklist if (propertiesToExclude != null && propertiesToExclude.Contains(settingField.Name)) { Logging.Debug("Property {0} matched to exclusion list, skipping", settingField.Name); continue; } //get the type of the field and make sure it actually exists in the list (it should) if (typesOfTypesInClass.Contains(settingField.FieldType)) { //since the type exists, it *should* save //https://stackoverflow.com/questions/2380467/c-dynamic-parse-from-system-type try { var converter = TypeDescriptor.GetConverter(settingField.FieldType); if (classInstance != null) { settingField.SetValue(classInstance, converter.ConvertFrom(settings[i].InnerText)); } else { settingField.SetValue(SettingsClass, converter.ConvertFrom(settings[i].InnerText)); } } catch (Exception e) { Logging.Debug("failed to load property to memory {0}{1}{2}", settingField.Name, Environment.NewLine, e.ToString()); } } else { throw new BadMemeException("hmmmmmm"); } } } return(true); }