Last active
October 24, 2019 15:52
-
-
Save derekantrican/a8bcd0b50c11bcb78f32f28839f16e9e to your computer and use it in GitHub Desktop.
An example about how to salvage an XML file when it is improperly formatted
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text.RegularExpressions; | |
using System.Xml.Linq; | |
using System.Xml.Serialization; | |
namespace SalvageXML | |
{ | |
public static class SalvageXML | |
{ | |
private static string settingsPath = Path.Combine(Common.UserSettings, "Settings.xml"); | |
public static Settings SalvageSettings(string xmlPath) | |
{ | |
Logger.Log("Attempting to upgrade settings", "SettingsUpgrader", updateLogLocations: false); | |
string xml = File.ReadAllText(xmlPath); | |
xml = xml.Replace(Environment.NewLine, ""); | |
Settings result = Settings.GetDefaultValues(); | |
foreach (PropertyInfo prop in typeof(Settings).GetProperties()) | |
{ | |
string match = Regex.Match(xml, $"<{prop.Name}>(.+)</{prop.Name}>").Groups[1].Value; | |
if (!string.IsNullOrEmpty(match)) | |
{ | |
try | |
{ | |
SetPropValue(match, prop, result); | |
Logger.Log($"Upgraded {prop.Name}", "SettingsUpgrader", updateLogLocations: false); | |
} | |
catch (Exception ex) | |
{ | |
Logger.LogError($"Could not parse setting from {match} ({{0}})", ex, updateLogLocations: false); | |
} | |
} | |
} | |
return result; | |
} | |
private static void SetPropValue(string value, PropertyInfo targetProp, dynamic targetClass) | |
{ | |
if (targetProp.GetValue(targetClass) is IList && targetProp.PropertyType.IsGenericType) | |
{ | |
IList list = targetProp.GetValue(targetClass) as IList; | |
list.Clear(); | |
Type listType = GetListType(targetProp); | |
foreach (Match match in Regex.Matches(value, "<.*?>(.*?)</.*?>", RegexOptions.IgnoreCase)) | |
{ | |
string itemString = match.Groups[1].Value; | |
list.Add(Convert.ChangeType(itemString, listType)); | |
} | |
targetProp.SetValue(targetClass, list); | |
} | |
else if (targetProp.PropertyType.IsEnum) | |
{ | |
//Convert enums that have changed values | |
if (targetProp.PropertyType == typeof(Enums.ReminderType) && | |
value == "Notification") | |
value = "Popup"; | |
else if (targetProp.PropertyType == typeof(Enums.CalendarVisibility) && | |
value == "CalendarDefault") | |
value = "Default"; | |
else if (targetProp.PropertyType == typeof(Enums.ChangeDurationMethod) && | |
value == "Specific") | |
value = "SpecificAmount"; | |
targetProp.SetValue(targetClass, Enum.Parse(targetProp.PropertyType, value)); | |
} | |
else if (targetProp.PropertyType == typeof(ModalSearch)) | |
{ | |
ModalSearch searchResult = ModalSearch.Instance; | |
foreach (PropertyInfo searchProp in typeof(ModalSearch).GetProperties()) | |
{ | |
string searchMatch = Regex.Match(value, $"<{searchProp.Name}>(.+)</{searchProp.Name}>", RegexOptions.IgnoreCase).Groups[1].Value; | |
if (!string.IsNullOrEmpty(searchMatch)) | |
SetPropValue(searchMatch, searchProp, searchResult); | |
} | |
targetProp.SetValue(targetClass, searchResult); | |
} | |
else if (targetProp.PropertyType == typeof(ModalFilter)) | |
{ | |
ModalFilter filterResult = ModalFilter.Instance; | |
foreach (PropertyInfo filterProp in typeof(ModalFilter).GetProperties()) | |
{ | |
string filterMatch = Regex.Match(value, $"<{filterProp.Name}>(.+)</{filterProp.Name}>", RegexOptions.IgnoreCase).Groups[1].Value; | |
if (!string.IsNullOrEmpty(filterMatch)) | |
SetPropValue(filterMatch, filterProp, filterResult); | |
} | |
targetProp.SetValue(targetClass, filterResult); | |
} | |
else if (targetProp.PropertyType == typeof(ModalEditor)) | |
{ | |
ModalEditor editorResult = ModalEditor.Instance; | |
foreach (PropertyInfo editorProp in typeof(ModalEditor).GetProperties()) | |
{ | |
string editorMatch = Regex.Match(value, $"<{editorProp.Name}>(.+)</{editorProp.Name}>", RegexOptions.IgnoreCase).Groups[1].Value; | |
if (!string.IsNullOrEmpty(editorMatch)) | |
SetPropValue(editorMatch, editorProp, editorResult); | |
} | |
targetProp.SetValue(targetClass, editorResult); | |
} | |
else | |
targetProp.SetValue(targetClass, Convert.ChangeType(value, targetProp.PropertyType)); | |
} | |
private static Type GetListType(PropertyInfo listProp) | |
{ | |
//Credit: https://stackoverflow.com/a/13608408/2246411 | |
Func<Type, Type> interfaceTest = new Func<Type, Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>) ? i.GetGenericArguments().Single() : null); | |
Type innerType = interfaceTest(listProp.PropertyType); | |
if (innerType != null) | |
return innerType; | |
foreach (Type i in listProp.PropertyType.GetInterfaces()) | |
{ | |
innerType = interfaceTest(i); | |
if (innerType != null) | |
return innerType; | |
} | |
return default(Type); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment