public static JObject TryToRestore([LocalizationRequired(false)] string damagedJson, JObjectRestorationScheme scheme)
{
var result = new JObject();
var input = Regex.Replace(damagedJson, @"\r?\n|\n", "\n").Trim();
foreach (var field in scheme.Fields)
{
var match = Regex.Match(input, $@"(?:""\s*{field.Name}\s*""|'\s*{field.Name}\s*'|{field.Name})\s*:\s*([\s\S]+)");
if (!match.Success)
{
continue;
}
var value = match.Groups[1].Value.Trim();
if (!field.IsMultiline)
{
value = value.Split('\n')[0];
value = Regex.Replace(value, @"\s*,?\s*(""\s*\w+\s*""|'\s*\w+\s*'|\w+)\s*:[\s\S]+|\s*}", "");
}
value = Regex.Replace(value, @"(?:\n?\s*,?\s*(""\s*\w+\s*""|'\s*\w+\s*'|\w+)\s*:|\s*})[\s\S]*$", "");
value = Regex.Replace(value.Trim(), @",$", "");
JToken processedValue;
if (value == "null")
{
processedValue = null;
}
else
{
switch (field.Type)
{
case JObjectRestorationScheme.FieldType.String:
case JObjectRestorationScheme.FieldType.NonNullString:
case JObjectRestorationScheme.FieldType.StringMultiline:
processedValue = DequoteString(value);
break;
case JObjectRestorationScheme.FieldType.Number:
var doubleValue = FlexibleParser.ParseDouble(value);
if (Equals(doubleValue % 1.0, 0.0))
{
processedValue = (long)doubleValue;
}
else
{
processedValue = doubleValue;
}
break;
case JObjectRestorationScheme.FieldType.Boolean:
processedValue = Regex.IsMatch(value, @"\b(true|on|yes|1)\b", RegexOptions.IgnoreCase);
break;
case JObjectRestorationScheme.FieldType.StringsArray:
processedValue = new JArray(
Regex.Split(value, @"^\s*\[|(?<!\\)""\s*,?\s*""|\s*(?:,\s*\n|\n\s*,?)\s*|\]\s*$")
.Select(DequoteString)
.Where(x => x.Length > 0 && x != "[" && x != "]")
.Cast <object>().ToArray());
break;
case JObjectRestorationScheme.FieldType.PairsArray:
processedValue = new JArray(
Regex.Split(value, @"^\s*\[|(?<!\\)""\s*\]?\s*,\s*\[??\s*""|\s*\]?\s*(?:,\s*\n|\n\s*,?)\s*\[?\s*|\]\s*$")
.Select(DequoteString)
.Where(x => x.Length > 0 && x != "[" && x != "]")
.Partition(2)
.Where(x => x.Length == 2)
.Select(x => new JArray(x.ToArray <object>()))
.Cast <object>().ToArray());
break;
default:
throw new ArgumentOutOfRangeException();
}
}
if (field.ParentName != null)
{
if (!(result[field.ParentName] is JObject obj))
{
result[field.ParentName] = obj = new JObject();
}
obj[field.Name] = processedValue;
}
else
{
result[field.Name] = processedValue;
}
}
foreach (var field in scheme.Fields.Where(x => x.Type == JObjectRestorationScheme.FieldType.NonNullString && !result.TryGetValue(x.Name, out _)))
{
if (field.ParentName != null)
{
if (!(result[field.ParentName] is JObject obj))
{
result[field.ParentName] = obj = new JObject();
}
obj[field.Name] = "";
}
else
{
result[field.Name] = "";
}
}
return(result);
}