43 internal static ConverterSettings ConverterSettings {
get;
set; }
44 internal static Dictionary<Type, MethodInfo> ToStringCache {
get;
set; } =
new Dictionary<Type, MethodInfo>();
46 public static List<dynamic> ConvertClassToList(dynamic myClass)
48 Type myClassType = myClass.GetType();
49 PropertyInfo[] properties = myClassType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
50 FieldInfo[] fields = myClassType.GetFields(BindingFlags.Public | BindingFlags.Instance);
51 List<dynamic> list =
new List<dynamic>();
57 if (IsDictionaryType(myClassType))
60 foreach (KeyValuePair<dynamic, dynamic> item
in myClass)
62 Type elementType = item.GetType();
64 if (
true == IsSimpleType(elementType))
68 myList = ConvertClassToList(item.Value);
75 else if (IsEnumerableType(myClassType))
78 foreach (var item
in myClass)
80 Type elementType = item.GetType();
82 if (
true == IsSimpleType(elementType))
86 myList = ConvertClassToList(item);
93 else if (
true == myClassType.IsArray)
95 var arrayList = myClass as Array;
96 int len = arrayList.Length;
98 for (
int idx = 0; idx < len; idx++)
100 var elementValue = arrayList.GetValue(idx);
101 var elementType = elementValue.GetType();
103 if (
true == IsSimpleType(elementType))
104 list.Add(elementValue);
107 myList = ConvertClassToList(elementValue);
114 if (
null != properties)
115 foreach (PropertyInfo property
in properties)
118 if (property.GetIndexParameters().Length > 0 ||
119 property.DeclaringType !=
null && property.DeclaringType.Namespace.StartsWith(
"System.Collections"))
124 Type propertyType =
property.PropertyType;
126 if (property.CanRead)
128 dynamic value =
null;
130 if (
true == propertyType.IsArray)
132 var arrayList =
property.GetValue(myClass) as Array;
133 int len = arrayList.Length;
135 for (
int idx = 0; idx < len; idx++)
137 var elementValue = arrayList.GetValue(idx);
138 var elementType = elementValue.GetType();
140 if (
true == IsSimpleType(elementType))
141 list.Add(elementValue);
142 else if (
true == IsEnumerableType(elementType))
144 IEnumerable enumeration = (IEnumerable)elementValue;
145 list.Add(enumeration);
147 else if (
true == IsDictionaryType(elementType))
149 myList = ConvertClassToList(elementValue);
158 value =
property.GetValue(myClass);
162 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Unable to getvalue of property [{property.Name}][{propertyType}]. Continuing and hoping for the best.", myClass, GPALObjectType.Other, ex);
167 if (IsBigIntegerType(propertyType) || IsVersionType(propertyType))
169 list.Add(value.ToString());
171 else if (IsSimpleType(propertyType))
173 list.Add(ConvertValue(value, propertyType));
175 else if (IsDictionaryType(propertyType))
177 list.Add(ConvertClassToList(value));
179 else if (IsEnumerableType(propertyType))
182 foreach (var item
in value)
184 var elementType = item.GetType();
186 if (
true == IsSimpleType(elementType))
188 else if (
true == IsComplexType(elementType))
190 string sign = item.Imaginary >= 0 ?
"+" :
"";
191 list.Add($
"{item.Real.ToString(CultureInfo.InvariantCulture)}{sign}{item.Imaginary.ToString(CultureInfo.InvariantCulture)}i");
195 myList = ConvertClassToList(item);
201 else if (
true == IsComplexType(propertyType))
203 string sign = value.Imaginary >= 0 ?
"+" :
"";
204 list.Add($
"{value.Real.ToString(CultureInfo.InvariantCulture)}{sign}{value.Imaginary.ToString(CultureInfo.InvariantCulture)}i");
208 list.Add(ConvertClassToList(value));
216 foreach (FieldInfo field
in fields)
219 object value = field.GetValue(myClass);
222 if (IsSimpleType(field.FieldType))
226 else if (IsDictionaryType(field.FieldType))
228 list.Add(ConvertClassToList(value));
230 else if (IsEnumerableType(field.FieldType))
232 foreach (
object item
in (IEnumerable<dynamic>)value)
234 myList = ConvertClassToList(item);
241 list.Add(ConvertClassToList(value));
253 public new bool Equals(
object x,
object y) => ReferenceEquals(x, y);
254 public int GetHashCode(
object obj) => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
256 public static Dictionary<object, dynamic> ConvertClassToDictionary(
259 HashSet<object> visited =
null)
262 bool isRootCall = visited ==
null;
268 Dictionary<object, dynamic> dict =
new Dictionary<object, dynamic>();
270 if (
false == IsCustomStructType(myClass?.GetType()) &&
false == IsTupleType(myClass?.GetType()) && myClass ==
null)
276 if (myClass.GetType().IsClass || myClass.GetType().IsInterface)
278 if (visited.TryGetValue(myClass, out dynamic fuggetAboutIt))
280 string referenceName =
string.Empty;
284 referenceName = myClass.Value;
290 referenceName = myClass.Name;
294 referenceName = myClass.GetType().Name;
298 return new Dictionary<object, dynamic>
300 { name, $
"Recursion to [{referenceName}]" }
306 visited.Add(myClass);
309 bool addedToVisited =
false == IsCustomStructType(myClass?.GetType()) &&
false == IsTupleType(myClass?.GetType()) && myClass !=
null && (myClass?.GetType().IsClass || myClass?.GetType().IsInterface);
313 Type myClassType = myClass?.GetType();
314 var properties = (from p in myClassType?.GetProperties(BindingFlags.Public | BindingFlags.Instance)
315 group p by p.Name into g
316 select g.OrderByDescending(t => t.DeclaringType == myClassType).First());
318 FieldInfo[] fields = myClassType?.GetFields(BindingFlags.Public | BindingFlags.Instance);
319 List<dynamic> list =
new List<dynamic>();
323 if (IsClassType(myClassType) &&
324 !myClassType.IsAbstract &&
325 !myClassType.IsInterface &&
326 !myClassType.IsArray &&
327 !IsSimpleType(myClassType) &&
328 !IsEnumerableType(myClassType) &&
329 !IsDictionaryType(myClassType) &&
330 !myClassType.IsDefined(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute),
false))
332 goto ProcessPropertiesAndFields;
336 if (IsDictionaryType(myClassType))
344 void AddEntry(dynamic entryKey, dynamic entryValue)
346 object outputKey = IsSimpleType(entryKey?.GetType()) ? (object)ConvertValue(entryKey, entryKey?.GetType()) : entryKey;
350 object outputValue = IsSimpleType(entryValue?.GetType())
351 ? (object)ConvertValue(entryValue, entryValue?.GetType())
352 : ConvertClassToDictionary(entryValue, entryKey?.ToString(), visited);
353 dict.Add(outputKey, outputValue);
358 var dictionary = (IDictionary)myClass;
359 foreach (var key
in dictionary.Keys)
361 AddEntry(key, dictionary[key]);
367 foreach (var item
in (IEnumerable)myClass)
369 var itemType = item.GetType();
370 var keyProp = itemType.GetProperty(
"Key");
371 var valueProp = itemType.GetProperty(
"Value");
372 if (keyProp !=
null && valueProp !=
null)
374 AddEntry(keyProp.GetValue(item), valueProp.GetValue(item));
378 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"Unable to convert class [{itemType}] Key [{keyProp?.Name}]/Value [{valueProp?.Name}] to dictionary.", myClass, GPALObjectType.Other);
384 else if (IsEnumerableType(myClassType))
388 foreach (dynamic item
in (IEnumerable)myClass)
390 Type elementType = item?.GetType();
391 if (elementType ==
null)
continue;
393 string keyName = $
"GPALKEY{idx:D4}";
395 if (IsSimpleType(elementType))
397 dict.Add(keyName, ConvertValue(item, elementType));
399 else if (IsComplexType(elementType))
401 string sign = item.Imaginary >= 0 ?
"+" :
"";
402 dict.Add(keyName, $
"{item.Real.ToString(CultureInfo.InvariantCulture)}{sign}{item.Imaginary.ToString(CultureInfo.InvariantCulture)}i");
404 else if (IsClassType(elementType) && !IsEnumerableType(elementType) && !IsDictionaryType(elementType) && !elementType.IsArray)
406 myDict = ConvertClassToDictionary(item,
"", visited);
407 dict.Add(keyName, myDict);
409 else if (IsDictionaryType(elementType) || IsEnumerableType(elementType) || elementType.IsArray)
411 myDict = ConvertClassToDictionary(item,
"", visited);
412 dict.Add(keyName, myDict);
416 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported element type [{elementType}] in enumerable [{myClassType}]", item, GPALObjectType.Other);
423 ProcessPropertiesAndFields:
424 if (properties !=
null)
426 foreach (PropertyInfo property
in properties)
428 if (property.GetIndexParameters().Length > 0)
433 if (property.DeclaringType == myClassType &&
434 property.DeclaringType.Namespace !=
null &&
435 property.DeclaringType.Namespace.StartsWith(
"System.Collections"))
440 Type propertyType =
property.PropertyType;
441 list =
new List<dynamic>();
443 if (property.CanRead)
445 dynamic value =
null;
448 value =
property.GetValue(myClass);
452 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Unable to getvalue of property [{property.Name}][{propertyType}]. Continuing and hoping for the best.", myClass, GPALObjectType.Other, ex);
455 if ((
object)value ==
null)
458 if (
false == IsTupleType(value?.GetType()))
460 if (propertyType.IsArray)
462 var arrayList = (Array)property.GetValue(myClass);
463 if (
null != arrayList)
464 for (
int idx = 0; idx < arrayList.Length; idx++)
466 var elementValue = arrayList.GetValue(idx);
467 var elementType = elementValue?.GetType() ?? typeof(
object);
470 if (IsSimpleType(elementType))
472 list.Add(ConvertValue(elementValue, elementType));
474 else if (IsEnumerableType(elementType))
476 list.Add((IEnumerable)elementValue);
478 else if (IsDictionaryType(elementType))
480 list.Add(ConvertClassToDictionary(elementValue, $
"{property.Name}{idx}", visited));
482 else if (IsCustomStructType(elementType) || IsClassType(elementType) || IsTupleType(elementType))
484 list.Add(ConvertClassToDictionary(elementValue, $
"{property.Name}{idx}", visited));
488 list.Add(ValueToString(arrayList));
491 dict.Add(property.Name, list);
493 else if (IsSimpleType(propertyType))
495 dict.Add(property.Name, ConvertValue(value, propertyType));
497 else if (IsDictionaryType(propertyType))
499 dict.Add(property.Name, ConvertClassToDictionary(value, $
"{propertyType}", visited));
501 else if (IsEnumerableType(propertyType))
505 foreach (var item
in value)
507 var elementType = item?.GetType() ?? typeof(
object);
508 if (
true == IsCustomStructType(item?.GetType()))
510 list.Add(ConvertClassToDictionary(item, $
"{property.Name}{idx}", visited));
516 else if (IsSimpleType(elementType))
518 list.Add(ConvertValue(item, elementType));
520 else if (IsComplexType(elementType))
522 string sign = item.Imaginary >= 0 ?
"+" :
"";
523 list.Add($
"{item.Real.ToString(CultureInfo.InvariantCulture)}{sign}{item.Imaginary.ToString(CultureInfo.InvariantCulture)}i");
525 else if (
true == IsGuidType(elementType))
527 list.Add(Guid.TryParse(item.ToString(), out Guid outGuid) ? outGuid : item);
529 else if (IsEnumerableType(elementType))
531 list.Add((IEnumerable)item);
535 list.Add(ConvertClassToDictionary(item, $
"{property.Name}{idx}", visited));
540 list.Add(ValueToString(value));
543 dict.Add(property.Name, list);
545 else if (IsComplexType(propertyType))
547 string sign = value.Imaginary >= 0 ?
"+" :
"";
548 dict.Add(property.Name, $
"{value.Real.ToString(CultureInfo.InvariantCulture)}{sign}{value.Imaginary.ToString(CultureInfo.InvariantCulture)}i");
550 else if (IsClassType(propertyType) || IsCustomStructType(value?.GetType()))
552 dict.Add(property.Name, ConvertClassToDictionary(value, property.Name, visited));
556 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unknown property type [{myClass.GetType()}][{propertyType}] detected. Adding [{value}] to dict.", myClass, GPALObjectType.Other);
557 dict.Add(property.Name, value);
562 dict.Add(property.Name, ConvertClassToDictionary(value, property.Name, visited));
570 foreach (FieldInfo field
in fields)
572 object value = field.GetValue(myClass);
573 if (
true == IsCustomStructType(value?.GetType()) || value !=
null)
575 if (IsSimpleType(field.FieldType))
577 dict.Add(field.Name, ConvertValue(value, field.FieldType));
579 else if (IsDictionaryType(field.FieldType))
581 dict.Add(field.Name, ConvertClassToDictionary(value, field.Name, visited));
583 else if (IsEnumerableType(field.FieldType))
586 list =
new List<dynamic>();
587 foreach (
object item
in (IEnumerable)value)
589 Type itemType = item?.GetType() ?? typeof(
object);
590 list.Add(IsSimpleType(itemType) ? ConvertValue(item, itemType) : ConvertClassToDictionary(item, $
"{itemType}{idx}", visited));
593 dict.Add(field.Name, list);
597 dict.Add(field.Name, ConvertClassToDictionary(value, field.Name, visited));
609 visited.Remove(myClass);
612 public static dynamic ConvertToClass(dynamic dictList, Type targetType, dynamic parentDictionary, List<string> columnNames)
615 Type createType = GetConcreteType(targetType);
616 dynamic myDictList = dictList;
617 int dictListLength = 0;
619 dictListLength = ObjectCopier.GetItemCount(
null, dictList);
621 if ((IsEnumerableObject(myDictList) || IsArray(myDictList)) && !IsDictionaryType(myDictList.GetType()) && 1 == dictListLength)
623 dynamic dictList2 = dictList[0];
624 if (
true == IsDictionaryType(dictList2.GetType()))
626 myDictList = dictList2;
630 dictListLength = ObjectCopier.GetItemCount(
null, myDictList);
634 if (
true == IsDictionaryType(myDictList.GetType()))
636 int nestedDictCount = 0;
638 foreach (var item
in myDictList)
640 if (
true == IsDictionaryType(item?.Value?.GetType()))
654 bool singleRowLooksIndexed =
false;
655 if (1 == dictListLength && 1 == nestedDictCount)
657 singleRowLooksIndexed =
true;
658 foreach (var soloRowItem
in myDictList)
660 string soloRowKeyStr = soloRowItem.Key?.ToString();
661 if (
false ==
int.TryParse(soloRowKeyStr, out
int soloRowKeyDummy))
663 singleRowLooksIndexed =
false;
668 if (nestedDictCount == dictListLength && (1 < dictListLength || singleRowLooksIndexed))
670 Type listType = typeof(List<>).MakeGenericType(createType);
671 IList result = (IList)InstantiateOne(listType);
675 if (
true == IsDictionaryType(createType))
677 IDictionary tmpDict = (IDictionary)InstantiateOne(createType);
678 Type dictKeyType = createType.GetGenericArguments()[0];
679 foreach (var item
in myDictList)
683 dynamic element = ConvertToClass(item.Value, ConverterSettings.OutputClassElementType, parentDictionary, columnNames);
684 object resolvedKey = IsSimpleType(dictKeyType)
685 ? ResolveSimpleDictionaryKey(item.Key, dictKeyType)
686 : (item.Key is IDictionary keyDict ? CreateObjectFromDictionary(keyDict, dictKeyType, parentDictionary, null) : item.Key);
687 tmpDict[resolvedKey] = element;
691 tmpDict = ConvertToClass(item.Value, createType, parentDictionary, columnNames);
696 else if (
true == IsEnumerableType(createType))
698 object container = InstantiateOne(createType, dictListLength);
708 Type rowElementType = createType.IsArray
709 ? createType.GetElementType()
710 : (createType.IsGenericType && createType.GetGenericArguments().Length > 0 ? createType.GetGenericArguments()[0] : ConverterSettings.OutputClassElementType);
712 foreach (var item
in myDictList)
714 object element = ConvertToClass(item.Value, rowElementType, parentDictionary, columnNames);
718 AddToList(container, element, index);
725 result.Add(container);
728 if (1 < result.Count)
737 dynamic unwrapped = myDictList;
738 if (myDictList.Count == 1)
740 foreach (var kvp
in myDictList)
742 if (kvp.Value is IDictionary)
744 string key = kvp.Key?.ToString() ??
string.Empty;
745 bool isRealProperty =
746 createType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
747 .Any(p =>
string.Equals(p.Name, key, StringComparison.OrdinalIgnoreCase))
748 || createType.GetFields(BindingFlags.Public | BindingFlags.Instance)
749 .Any(f =>
string.Equals(f.Name, key, StringComparison.OrdinalIgnoreCase));
751 unwrapped = kvp.Value;
756 return CreateObjectFromDictionary(unwrapped, createType, parentDictionary, columnNames);
759 else if (IsEnumerableObject(myDictList) || IsArray(myDictList))
761 Type listType = typeof(List<>).MakeGenericType(createType);
762 IList result = (IList)InstantiateOne(listType);
763 dynamic createdType = InstantiateOne(createType);
767 foreach (var item
in myDictList)
769 object element = CreateObjectFromDictionary(item, ConverterSettings.OutputClassElementType, parentDictionary, columnNames);
770 AddToList(createdType, element, count++);
773 result.Add(createdType);
775 if (1 < result.Count)
783 return CreateObjectFromDictionary(myDictList, createType, parentDictionary, columnNames);
787 static dynamic lastParentDictionary =
null;
788 static List<string> lastErrorMessage =
new List<string>();
789 static bool supressedMessage =
false;
790 [System.ThreadStatic]
791 private static HashSet<object> _dictVisited;
800 internal static object CreateObjectFromDictionary(dynamic dictionary, Type targetType, dynamic parentDictionary, List<string> columnNames)
804 bool isRoot = _dictVisited ==
null;
806 bool addedToVisited =
false;
807 if (dictionary !=
null && !(targetType?.IsValueType ==
true))
809 object dictRef = (object)dictionary;
810 if (!_dictVisited.Add(dictRef))
811 return InstantiateOne(targetType);
812 addedToVisited =
true;
815 dynamic objOrList =
null;
816 string kvpName =
string.Empty;
817 string kvpPropertyOrFieldName =
string.Empty;
818 dynamic kvpValue =
null;
819 PropertyInfo kvpProperty =
null;
820 FieldInfo kvpField =
null;
821 Type kvpPropertyType =
null;
822 Type priorKvpPropertyType =
null;
823 Type kvpFieldType =
null;
824 object itemobj =
null;
825 int noPropertyOrField = 0;
827 bool isEnumerable = IsEnumerableType(targetType);
831 if (dictionary is IList cofdSingleList && cofdSingleList.Count == 1 && cofdSingleList[0] is IDictionary cofdInnerDict
832 && !isEnumerable && !targetType.IsArray && !IsDictionaryType(targetType))
834 dictionary = cofdInnerDict;
838 bool HasParentChanged(dynamic lastParent, dynamic currentParent)
842 if (currentParent ==
null)
846 if (lastParent ==
null)
850 if (lastParent.GetType() != currentParent.GetType())
855 return !ReferenceEquals(lastParent, currentParent);
858 if (
true == HasParentChanged(lastParentDictionary, parentDictionary))
860 lastParentDictionary = parentDictionary;
861 lastErrorMessage.Clear();
862 supressedMessage =
false;
865 if (
true == isEnumerable || targetType.IsArray)
866 count = ObjectCopier.GetItemCount(
null, dictionary);
868 if (IsComplexType(targetType))
869 return ComplexFromDict(dictionary);
874 if (targetType == typeof(
object) && dictionary is IDictionary)
879 objOrList = ConverterHelper.InstantiateOne(targetType, count);
883 Type listType = typeof(List<>).MakeGenericType(targetType);
884 objOrList = ActivatorHelper.CreateInstance(listType);
887 if (IsDictionaryType(targetType))
889 if (dictionary is IDictionary dictInput)
891 Type keyType = targetType.GetGenericArguments()[0];
892 Type valueType = targetType.GetGenericArguments()[1];
893 IDictionary dict = (IDictionary)objOrList;
894 foreach (dynamic kvp
in dictInput)
896 object key = IsSimpleType(keyType)
897 ? ResolveSimpleDictionaryKey(kvp.Key, keyType)
898 : (kvp.Key is IDictionary keyDict ? CreateObjectFromDictionary(keyDict, keyType, dictInput, null) : kvp.Key);
899 object value = kvp.Value?.ToString().Trim(
'"') ??
string.Empty;
904 if (kvp.Value !=
null && valueType.IsAssignableFrom(kvp.Value.GetType()))
909 else if (kvp.Value is IDictionary && IsClassType(valueType) && valueType != typeof(
string))
911 value = ConvertToClass(kvp.Value, valueType, dictInput, columnNames);
914 else if (IsDictionaryType(valueType) && kvp.Value is IDictionary)
916 value = CreateObjectFromDictionary(kvp.Value, valueType, dictInput, columnNames);
919 else if (IsSimpleType(valueType) || IsNullableType(valueType))
921 value = ConvertValue(kvp.Value, valueType);
924 else if (IsEnumerableType(valueType) && valueType != typeof(
string))
926 Type elemType = valueType.GetGenericArguments().Length > 0
927 ? valueType.GetGenericArguments()[0]
928 : valueType.GetElementType();
929 if (elemType !=
null)
931 dynamic resultList = InstantiateOne(valueType);
933 IEnumerable innerEnum = kvp.Value is IEnumerable enumer && !(kvp.Value is string)
935 : new object[] { kvp.Value };
936 foreach (var innerItem
in innerEnum)
938 if (innerItem is IDictionary innerDict && IsClassType(elemType) && elemType != typeof(
string))
939 AddToList(resultList, CreateObjectFromDictionary(innerDict, elemType, dictInput, columnNames), i2++);
940 else if (IsSimpleType(elemType) || IsNullableType(elemType))
941 AddToList(resultList, ConvertValue(innerItem, elemType), i2++);
942 else if (IsBigIntegerType(elemType))
944 if (innerItem is
string biNullStr2 && IsNullableType(elemType) && biNullStr2.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
945 AddToList(resultList,
null, i2++);
948 try { AddToList(resultList, AsBigInteger(innerItem), i2++); }
949 catch { GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid BigInteger format [{innerItem}] for element type [{elemType}]", innerItem, GPALObjectType.Other); }
953 AddToList(resultList, innerItem, i2++);
963 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to add key[{key}]-value[{value}] pair to dictionary [{targetType}]", kvp, GPALObjectType.Other, ex);
970 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Expected IDictionary input for [{targetType}]", dictionary, GPALObjectType.None);
975 else if (
true == isEnumerable)
977 Type elementType = targetType.IsArray ? targetType.GetElementType() : (targetType.GetGenericArguments().Length > 0 ? targetType.GetGenericArguments()[0] : targetType);
978 if (dictionary is IDictionary dictInput)
980 bool isIndexed = dictInput.Keys.Cast<dynamic>().All(key =>
int.TryParse(key?.ToString(), out
int dummy));
983 dynamic keys = dictInput.Keys;
985 foreach (var key
in keys)
989 object value = dictInput[key];
990 if (value is IDictionary classDict && IsClassType(elementType) && elementType != typeof(
string))
992 object classInstance = CreateObjectFromDictionary(classDict, elementType, keys, columnNames);
993 if (classInstance !=
null)
995 AddToList(objOrList, classInstance, count++);
999 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"ConvertToClass returned null for [{elementType}] with dict [{classDict}]", classDict, GPALObjectType.Other);
1002 else if (IsSimpleType(elementType) && (value ==
null || IsSimpleType(value.GetType())))
1005 object convertedValue = ConvertValue(value, elementType);
1006 AddToList(objOrList, convertedValue, count++);
1010 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Expected IDictionary for TestClass or simple type, got [{value?.GetType()}] for element type [{elementType}]", key, GPALObjectType.Other);
1013 catch (Exception ex)
1015 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to add value to enumerable [{targetType}]", key, GPALObjectType.Other, ex);
1021 foreach (dynamic kvp
in dictionary)
1026 dynamic value = kvp.Value;
1029 if (elementType.IsValueType && !IsNullableType(elementType))
1031 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Null value not allowed for non-nullable type [{elementType}]", kvp, GPALObjectType.Other);
1035 AddToList(objOrList,
null, count++);
1038 else if (elementType.IsAssignableFrom(value.GetType()))
1040 AddToList(objOrList, value, count++);
1042 else if (IsCustomStructType(elementType))
1045 var converter = TypeDescriptor.GetConverter(elementType);
1046 if (converter !=
null && converter.CanConvertFrom(value.GetType()))
1048 value = converter.ConvertFrom(value);
1049 AddToList(objOrList, value, count++);
1055 AddToList(objOrList, value, count++);
1059 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported struct type [{value.GetType()}] for element type [{elementType}]", kvp, GPALObjectType.Other);
1063 else if (value is IDictionary nestedDict)
1065 if (IsClassType(elementType) && elementType != typeof(
string))
1067 bool nestedIsIndexed = nestedDict.Keys.Cast<dynamic>().All(k =>
int.TryParse(k?.ToString(), out
int dummy));
1068 if (!nestedIsIndexed)
1070 value = ConvertToClass(nestedDict, elementType, dictionary, columnNames);
1071 AddToList(objOrList, value, count++);
1074 else if (IsDictionaryType(elementType) || IsCustomStructType(elementType) || IsTupleType(elementType))
1076 value = CreateObjectFromDictionary(nestedDict, elementType, dictionary, columnNames);
1077 AddToList(objOrList, value, count++);
1081 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Dictionary value [{value.GetType()}] incompatible with element type [{elementType}]", kvp, GPALObjectType.Other);
1084 else if (IsEnumerableType(elementType) && value is IEnumerable enumValue)
1086 value = CreateObjectFromDictionary(enumValue, elementType, dictionary, columnNames);
1087 AddToList(objOrList, value, count++);
1089 else if (IsSimpleType(elementType) || IsNullableType(elementType))
1091 if (IsSimpleType(value.GetType()) || IsNullableType(value.GetType()))
1093 value = ConvertValue(value, elementType);
1094 AddToList(objOrList, value, count++);
1098 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Value type [{value.GetType()}] incompatible with simple/nullable element type [{elementType}]", kvp, GPALObjectType.Other);
1101 else if (IsComplexType(elementType))
1103 if (value is
string complexStr)
1105 value = ParseComplex(complexStr);
1106 AddToList(objOrList, value, count++);
1108 else if (value is Complex complexValue)
1110 AddToList(objOrList, complexValue, count++);
1114 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Value type [{value.GetType()}] incompatible with complex/nullable element type [{elementType}]", kvp, GPALObjectType.Other);
1117 else if (IsBigIntegerType(elementType))
1119 if (value is
string biNullStr && IsNullableType(elementType) && biNullStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1120 AddToList(objOrList,
null, count++);
1123 try { AddToList(objOrList, AsBigInteger(value), count++); }
1124 catch { GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid BigInteger format [{value}] for element type [{elementType}]", kvp, GPALObjectType.Other); }
1127 else if (IsVersionType(elementType))
1129 if (value is
string versionStr && System.Version.TryParse(versionStr, out System.Version version))
1131 AddToList(objOrList, version, count++);
1135 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Version format [{value}] for element type [{elementType}]", kvp, GPALObjectType.Other);
1138 else if (
true == IsIpAddressType(elementType))
1140 if (value is
string ipStr && IPAddress.TryParse(ipStr, out IPAddress ipAddress))
1142 AddToList(objOrList, ipAddress, count++);
1146 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid IPAddress format [{value}] for element type [{elementType}]", kvp, GPALObjectType.Other);
1149 else if (
true == IsUriType(elementType))
1151 if (value is
string uriStr && Uri.TryCreate(uriStr, UriKind.Absolute, out Uri uri))
1153 AddToList(objOrList, uri, count++);
1157 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Uri format [{value}] for element type [{elementType}]", kvp, GPALObjectType.Other);
1160 else if (
true == IsGuidType(elementType))
1162 if (value is
string guidStr && IsNullableType(elementType) && guidStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1163 AddToList(objOrList,
null, count++);
1164 else if (value is
string guidStr2 && Guid.TryParse(guidStr2, out Guid guid))
1165 AddToList(objOrList, guid, count++);
1167 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Guid format [{value}] for element type [{elementType}]", kvp, GPALObjectType.Other);
1169 else if (
true == IsTimeSpanType(elementType))
1171 if (value is
string timeSpanStr && IsNullableType(elementType) && timeSpanStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1172 AddToList(objOrList,
null, count++);
1173 else if (value is
string timeSpanStr2 && TimeSpan.TryParse(timeSpanStr2, out TimeSpan timeSpan))
1174 AddToList(objOrList, timeSpan, count++);
1176 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid TimeSpan format [{value}] for element type [{elementType}]", kvp, GPALObjectType.Other);
1178 else if (IsClassType(elementType) && elementType != typeof(
string))
1181 var converter = TypeDescriptor.GetConverter(elementType);
1182 if (converter !=
null && converter.CanConvertFrom(value.GetType()))
1184 value = converter.ConvertFrom(value);
1185 AddToList(objOrList, value, count++);
1189 AddToList(objOrList, value, count++);
1195 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported value type [{value.GetType()}] for element type [{elementType}]", kvp, GPALObjectType.Other);
1198 catch (Exception ex)
1200 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to add value to enumerable [{targetType}]", kvp, GPALObjectType.Other, ex);
1206 else if (dictionary is IEnumerable && !IsDictionaryType(dictionary.GetType()))
1208 IEnumerable enumInput = (IEnumerable)dictionary;
1209 foreach (dynamic item
in enumInput)
1213 dynamic value = item;
1216 if (elementType.IsValueType && !IsNullableType(elementType))
1218 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Null value not allowed for non-nullable type [{elementType}]", item, GPALObjectType.Other);
1222 AddToList(objOrList,
null, count++);
1225 else if (elementType.IsAssignableFrom(value.GetType()))
1227 AddToList(objOrList, value, count++);
1229 else if (elementType.IsArray)
1231 var arrayElementType = elementType.GetElementType();
1232 var listType = typeof(List<>).MakeGenericType(arrayElementType);
1233 var tempList = (IList)InstantiateOne(listType);
1234 if (value is IEnumerable arrayValues)
1236 foreach (var arrayItem
in arrayValues)
1238 var convertedItem = CreateObjectFromDictionary(arrayItem, arrayElementType, enumInput, columnNames);
1239 tempList.Add(convertedItem);
1242 value = tempList.Cast<
object>().ToArray();
1243 AddToList(objOrList, value, count++);
1245 else if (IsEnumerableType(elementType) && value is IEnumerable enumValue)
1247 value = CreateObjectFromDictionary(enumValue, elementType, enumInput, columnNames);
1248 AddToList(objOrList, value, count++);
1250 else if (IsComplexType(elementType))
1252 if (value is
string complexStr)
1254 value = ParseComplex(complexStr);
1255 AddToList(objOrList, value, count++);
1257 else if (value is Complex complexValue)
1259 AddToList(objOrList, complexValue, count++);
1261 else if (value is IDictionary || (value is IEnumerable && !(value is
string)))
1263 AddToList(objOrList, ComplexFromDict(value), count++);
1267 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Value type [{value.GetType()}] incompatible with complex/nullable element type [{elementType}]", value, GPALObjectType.Other);
1270 else if (IsBigIntegerType(elementType))
1272 if (value is
string biNullStr4 && IsNullableType(elementType) && biNullStr4.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1273 AddToList(objOrList,
null, count++);
1276 try { AddToList(objOrList, AsBigInteger(value), count++); }
1277 catch { GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid BigInteger format [{value}] for element type [{elementType}]", item, GPALObjectType.Other); }
1280 else if (IsVersionType(elementType))
1282 if (value is
string versionStr && System.Version.TryParse(versionStr, out System.Version version))
1284 AddToList(objOrList, version, count++);
1288 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Version format [{value}] for element type [{elementType}]", item, GPALObjectType.Other);
1291 else if (
true == IsIpAddressType(elementType))
1293 if (value is
string ipStr && IPAddress.TryParse(ipStr, out IPAddress ipAddress))
1295 AddToList(objOrList, ipAddress, count++);
1299 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid IPAddress format [{value}] for element type [{elementType}]", item, GPALObjectType.Other);
1302 else if (
true == IsUriType(elementType))
1304 if (value is
string uriStr && Uri.TryCreate(uriStr, UriKind.Absolute, out Uri uri))
1306 AddToList(objOrList, uri, count++);
1310 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Uri format [{value}] for element type [{elementType}]", item, GPALObjectType.Other);
1313 else if (
true == IsGuidType(elementType))
1315 if (value is
string guidStr && IsNullableType(elementType) && guidStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1316 AddToList(objOrList,
null, count++);
1317 else if (value is
string guidStr2 && Guid.TryParse(guidStr2, out Guid guid))
1318 AddToList(objOrList, guid, count++);
1320 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Guid format [{value}] for element type [{elementType}]", item, GPALObjectType.Other);
1322 else if (
true == IsTimeSpanType(elementType))
1324 if (value is
string timeSpanStr && IsNullableType(elementType) && timeSpanStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1325 AddToList(objOrList,
null, count++);
1326 else if (value is
string timeSpanStr2 && TimeSpan.TryParse(timeSpanStr2, out TimeSpan timeSpan))
1327 AddToList(objOrList, timeSpan, count++);
1329 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid TimeSpan format [{value}] for element type [{elementType}]", item, GPALObjectType.Other);
1331 else if (
true == IsBooleanType(elementType))
1333 if (value is
string boolStr)
1335 if (IsNullableType(elementType) && boolStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1337 AddToList(objOrList,
null, count++);
1339 else if (
bool.TryParse(boolStr.Trim(), out
bool parsedBool))
1341 AddToList(objOrList, parsedBool, count++);
1346 string lower = boolStr.Trim().ToLowerInvariant();
1347 if (lower ==
"1" || lower ==
"yes" || lower ==
"on" || lower ==
"true")
1349 AddToList(objOrList,
true, count++);
1351 else if (lower ==
"0" || lower ==
"no" || lower ==
"off" || lower ==
"false")
1353 AddToList(objOrList,
false, count++);
1357 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1358 $
"Invalid boolean format [{value}] for element type [{elementType}]",
1359 item, GPALObjectType.Other);
1363 else if (value is
bool directBool)
1365 AddToList(objOrList, directBool, count++);
1369 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1370 $
"Invalid type for boolean: expected string or bool, got [{value?.GetType()}]",
1371 item, GPALObjectType.Other);
1374 else if (IsSimpleType(elementType) || IsNullableType(elementType))
1376 if (IsSimpleType(value.GetType()) || IsNullableType(value.GetType()))
1378 value = ConvertValue(value, elementType);
1379 AddToList(objOrList, value, count++);
1383 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Value type [{value.GetType()}] incompatible with simple/nullable element type [{elementType}]", item, GPALObjectType.Other);
1386 else if (IsClassType(elementType) && elementType != typeof(
string))
1388 var converter = TypeDescriptor.GetConverter(elementType);
1389 if (converter !=
null && converter.CanConvertFrom(value.GetType()))
1391 value = converter.ConvertFrom(value);
1392 AddToList(objOrList, value, count++);
1398 AddToList(objOrList, value, count++);
1402 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported class type [{value.GetType()}] for element type [{elementType}]", item, GPALObjectType.Other);
1406 else if (IsCustomStructType(elementType))
1409 if (value is IList svl2 && svl2.Count == 1 && svl2[0] is IDictionary svd2)
1411 if (value is IDictionary svStructDict2)
1413 value = CreateObjectFromDictionary(svStructDict2, elementType, enumInput, columnNames);
1414 AddToList(objOrList, value, count++);
1418 var converter = TypeDescriptor.GetConverter(elementType);
1419 if (converter !=
null && converter.CanConvertFrom(value.GetType()))
1421 value = converter.ConvertFrom(value);
1422 AddToList(objOrList, value, count++);
1424 else if (
false == IsSimpleType(value.GetType()))
1428 AddToList(objOrList, value, count++);
1432 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported struct type [{value.GetType()}] for element type [{elementType}]", item, GPALObjectType.Other);
1437 else if (value is IDictionary nestedDict)
1439 if (IsClassType(elementType) && elementType != typeof(
string))
1441 value = ConvertToClass(nestedDict, elementType, enumInput, columnNames);
1442 AddToList(objOrList, value, count++);
1444 else if (IsDictionaryType(elementType) || IsTupleType(elementType))
1446 value = CreateObjectFromDictionary(nestedDict, elementType, enumInput, columnNames);
1447 AddToList(objOrList, value, count++);
1451 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Dictionary value [{value.GetType()}] incompatible with element type [{elementType}]", item, GPALObjectType.Other);
1456 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported item type [{value?.GetType()}] for element type [{elementType}]", item, GPALObjectType.Other);
1459 catch (Exception ex)
1461 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to add item to enumerable [{targetType}]", item, GPALObjectType.Other, ex);
1466 else if (!(dictionary is IDictionary inputDict))
1468 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Expected IDictionary input for [{targetType}]", dictionary, GPALObjectType.None);
1473 foreach (dynamic kvp
in inputDict)
1475 noPropertyOrField = 0;
1476 priorKvpPropertyType = kvpPropertyType;
1478 if (IsKeyValuePairType(kvp.GetType()))
1480 if (
null != columnNames && columnIdx < columnNames.Count)
1481 kvpName = columnNames[columnIdx++].Trim(
'"');
1484 string rawKey = kvp.Key?.ToString().Trim(
'"') ??
string.Empty;
1485 if (rawKey.StartsWith(
"GPALKEY", StringComparison.Ordinal))
1486 rawKey = rawKey.Substring(7);
1487 else if (System.Text.RegularExpressions.Regex.IsMatch(rawKey,
@"^GPAL\d+_"))
1488 rawKey = System.Text.RegularExpressions.Regex.Replace(rawKey,
@"^GPAL\d+_",
"");
1492 if (typeof(
string) == kvp.Value?.GetType())
1493 kvpValue = kvp.Value?.ToString().Trim(
'"');
1495 kvpValue = kvp.Value;
1497 kvpProperty = targetType.GetProperty(kvpName)
1498 ?? targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
1499 .FirstOrDefault(p => p.Name.Equals(kvpName, StringComparison.OrdinalIgnoreCase));
1501 kvpField = targetType.GetField(kvpName)
1502 ?? targetType.GetFields(BindingFlags.Public | BindingFlags.Instance)
1503 .FirstOrDefault(f => f.Name.Equals(kvpName, StringComparison.OrdinalIgnoreCase));
1505 if (kvpProperty !=
null)
1507 kvpPropertyType = kvpProperty.PropertyType;
1508 kvpPropertyOrFieldName = kvpProperty.Name;
1510 if (kvpField !=
null)
1512 kvpFieldType = kvpField.FieldType;
1513 kvpPropertyOrFieldName = kvpField.Name;
1521 if (kvpProperty ==
null && kvpField ==
null)
1523 kvpPropertyType = targetType;
1524 noPropertyOrField = 2;
1527 if (kvpProperty !=
null && kvpProperty.CanWrite && kvpProperty.Name.Equals(kvpName, StringComparison.OrdinalIgnoreCase))
1529 if (IsDictionaryType(kvpPropertyType))
1531 if (kvpValue is IList kvpSingleWrap && kvpSingleWrap.Count == 1 && kvpSingleWrap[0] is IDictionary kvpUnwrappedInner)
1532 kvpValue = kvpUnwrappedInner;
1533 if (kvpValue ==
null || (kvpValue is
string kvpDictNullStr && kvpDictNullStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase)))
1537 else if (kvpValue is IDictionary valueDict)
1539 IDictionary propDict = (IDictionary)ConverterHelper.InstantiateOne(kvpPropertyType);
1540 Type keyType = kvpPropertyType.GetGenericArguments()[0];
1541 Type valueType = kvpPropertyType.GetGenericArguments()[1];
1543 foreach (dynamic valueKvp
in valueDict)
1547 object key = IsSimpleType(keyType)
1548 ? ResolveSimpleDictionaryKey(valueKvp.Key, keyType)
1549 : (valueKvp.Key is IDictionary valueKeyDict ? CreateObjectFromDictionary(valueKeyDict, keyType, valueDict, null) : valueKvp.Key);
1552 if (IsKeyValuePairType(valueKvp.Value?.GetType()))
1554 var nestedKvp = valueKvp.Value;
1555 value = IsDictionaryType(valueType)
1556 ? CreateObjectFromDictionary(new Dictionary<object, object> { { nestedKvp.Key, nestedKvp.Value } }, valueType, valueDict, columnNames)
1557 : ConvertValue(nestedKvp.Value, valueType,
null, kvpName);
1559 else if (IsDictionaryType(valueKvp.Value?.GetType()))
1564 if (valueType == typeof(
object) || valueType.IsAssignableFrom(valueKvp.Value.GetType()))
1565 value = valueKvp.Value;
1567 value = CreateObjectFromDictionary(valueKvp.Value, valueType, valueDict, columnNames);
1571 value = ConvertValue(valueKvp.Value, valueType,
null, kvpName);
1574 if (value ==
null || valueType.IsAssignableFrom(value.GetType()))
1575 propDict[key] = value;
1577 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unable to convert [{DescribeForLog(value)}] to [{valueType.Name}] for [{kvpName}]", valueKvp, GPALObjectType.None);
1579 catch (Exception ex)
1581 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to process key-value pair for [{kvpName}]", valueKvp, GPALObjectType.None, ex);
1584 if (objOrList.GetType().IsValueType)
1586 string bfName = $
"<{kvpProperty.Name}>k__BackingField";
1587 objOrList = GetSetStructMethod(objOrList.GetType()).Invoke(
null,
new object[] { (object)objOrList, bfName, propDict });
1590 kvpProperty.SetValue(objOrList, propDict);
1594 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Value for property [{kvpName}] is not a dictionary", kvpValue, GPALObjectType.None);
1597 else if (IsEnumerableType(kvpPropertyType) || kvpPropertyType.IsArray)
1599 Type elementType = kvpPropertyType.IsArray ? kvpPropertyType.GetElementType() : kvpPropertyType.GetGenericArguments()[0];
1600 itemobj = ConverterHelper.InstantiateOne(elementType);
1603 if (kvpValue is
string kvpNullCollStr && kvpNullCollStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1607 else if (kvpValue is IDictionary collapsedDict && !IsDictionaryType(elementType) && (IsClassType(elementType) || IsCustomStructType(elementType) || IsTupleType(elementType)))
1613 bool looksLikeIndexedArray = collapsedDict.Keys.Cast<
object>().All(k =>
int.TryParse(k?.ToString(), out _));
1614 object innerList = ConverterHelper.InstantiateOne(kvpPropertyType, looksLikeIndexedArray ? collapsedDict.Count : 1);
1615 if (looksLikeIndexedArray)
1618 foreach (var indexedKey
in collapsedDict.Keys)
1620 object indexedElement = collapsedDict[indexedKey] is IDictionary indexedElementDict
1621 ? CreateObjectFromDictionary(indexedElementDict, elementType, parentDictionary, columnNames)
1622 : ConvertValue(collapsedDict[indexedKey], elementType);
1623 AddToList(innerList, indexedElement, indexedIdx++);
1628 object element = CreateObjectFromDictionary(collapsedDict, elementType, parentDictionary, columnNames);
1629 AddToList(innerList, element, 0);
1631 if (ObjectCopier.GetItemCount(kvpProperty, innerList) > 0)
1633 if (IsEnumerableObject(objOrList) || objOrList.GetType().IsArray)
1634 AddToList(objOrList, innerList, count++);
1635 else if (objOrList.GetType().IsValueType)
1637 string bfName = $
"<{kvpProperty.Name}>k__BackingField";
1638 objOrList = GetSetStructMethod(objOrList.GetType()).Invoke(
null,
new object[] { (object)objOrList, bfName, innerList });
1641 kvpProperty.SetValue(objOrList, innerList);
1644 else if (IsEnumerableObject(kvpValue) || IsArray(kvpValue))
1647 Type innerElementType =
null;
1651 innerElementType = kvpValue.IsArray ? kvpValue.GetElementType() : kvpValue.GetGenericArguments()[0];
1659 innerElementType = IsTupleType(elementType) ? null : elementType.GetGenericArguments()[0];
1663 innerElementType = elementType;
1667 object innerList = ConverterHelper.InstantiateOne(kvpPropertyType, ObjectCopier.GetItemCount(kvpProperty, kvpValue));
1668 foreach (dynamic kvpValueItem
in (IEnumerable)kvpValue)
1670 Type kvpValueItemType = kvpValueItem.GetType();
1671 object element =
null;
1673 if (
false == IsKeyValuePairType(kvpValueItemType) &&
false == IsCustomStructType(kvpValueItemType) && kvpValueItem ==
null)
1675 if (elementType.IsValueType && !IsNullableType(elementType))
1677 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Null value not allowed for non-nullable type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1681 AddToList(innerList,
null, count++);
1684 else if (elementType.IsAssignableFrom(kvpValueItem.GetType()))
1686 AddToList(innerList, kvpValueItem, count++);
1688 else if (kvpValueItem is IDictionary nestedDict)
1690 if ((IsCustomStructType(elementType) || IsClassType(elementType)) && elementType != typeof(
string))
1692 element = ConvertToClass(nestedDict, elementType, parentDictionary, columnNames);
1693 AddToList(innerList, element, count++);
1695 else if (IsDictionaryType(kvpValueItem.GetType()) || IsDictionaryType(elementType) ||
true == IsEnumerableType(elementType))
1697 element = CreateObjectFromDictionary(nestedDict, elementType, parentDictionary, columnNames);
1698 AddToList(innerList, element, count++);
1702 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Dictionary value [{kvpValueItem.GetType()}] incompatible with element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1705 else if (IsKeyValuePairType(kvpValueItemType))
1707 var nestedKvp = kvpValueItem;
1708 Type valueType = elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
1709 ? elementType.GetGenericArguments()[1]
1712 if (IsDictionaryType(elementType))
1714 element = CreateObjectFromDictionary(
new Dictionary<object, object> { { nestedKvp.Key, nestedKvp.Value } }, elementType, parentDictionary, columnNames);
1716 else if (IsComplexType(valueType))
1718 element = ParseComplex(nestedKvp.Value?.ToString());
1720 else if (IsSimpleType(valueType))
1722 element = ConvertValue(nestedKvp.Value?.ToString(), valueType,
null, kvpName);
1728 element = CreateObjectFromDictionary(nestedKvp.Value, elementType, parentDictionary, columnNames);
1732 element = nestedKvp.Value;
1735 AddToList(innerList, element, count++);
1737 else if (IsTupleType(elementType) && kvpValueItem is IList tupleBundledList)
1741 foreach (dynamic tupleSubItem
in tupleBundledList)
1743 if (tupleSubItem is IDictionary tupleSubDict)
1745 element = CreateObjectFromDictionary(tupleSubDict, elementType, parentDictionary, columnNames);
1746 AddToList(innerList, element, count++);
1750 else if (IsComplexType(elementType) || (
true == IsComplexType(innerElementType) && typeof(
string) == kvpValueItemType))
1752 if (kvpValueItem is
string complexStr)
1754 element = ParseComplex(complexStr);
1755 AddToList(innerList, element, count++);
1757 else if (kvpValueItem is Complex complexValue)
1759 AddToList(innerList, complexValue, count++);
1761 else if (
true == IsDictionaryType(kvpValueItemType) ||
true == IsEnumerableType(kvpValueItemType))
1764 element = CreateObjectFromDictionary(kvpValueItem, elementType, kvpValue, columnNames);
1765 AddToList(innerList, element, count++);
1770 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Complex format [{kvpValueItem}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1773 else if (((IsDictionaryType(kvpValueItemType) || IsEnumerableType(elementType) || elementType.IsArray)) && typeof(
string) != kvpValueItemType)
1775 if (IsEnumerableType(elementType) && !(kvpValueItem is IEnumerable))
1778 Type innerScalarType = elementType.IsGenericType ? elementType.GetGenericArguments()[0] : typeof(
object);
1779 dynamic singleItemList = InstantiateOne(elementType);
1780 AddToList(singleItemList, ConvertValue(kvpValueItem, innerScalarType,
null, kvpName), 0);
1781 element = singleItemList;
1785 element = CreateObjectFromDictionary(kvpValueItem, elementType, parentDictionary, columnNames);
1787 AddToList(innerList, element, count++);
1789 else if (IsBigIntegerType(elementType) || IsBigIntegerType(innerElementType))
1791 Type biNullableCheck2 = IsBigIntegerType(innerElementType) ? innerElementType : elementType;
1792 if (kvpValueItem is
string biNullStr3 && IsNullableType(biNullableCheck2) && biNullStr3.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1793 AddToList(innerList,
null, count++);
1796 try { AddToList(innerList, AsBigInteger(kvpValueItem), count++); }
1797 catch { GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid BigInteger format [{kvpValueItem}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other); }
1800 else if (IsVersionType(elementType) || IsVersionType(innerElementType))
1802 if (kvpValueItem is
string versionStr && System.Version.TryParse(versionStr, out System.Version version))
1804 AddToList(innerList, version, count++);
1808 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Version format [{kvpValueItem}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1811 else if (
true == IsIpAddressType(elementType) ||
true == IsIpAddressType(innerElementType))
1813 if (kvpValueItem is
string ipStr && IPAddress.TryParse(ipStr, out IPAddress ipAddress))
1815 AddToList(innerList, ipAddress, count++);
1819 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid IPAddress format [{kvpValueItem}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1822 else if (
true == IsUriType(elementType) ||
true == IsUriType(innerElementType) )
1824 if (kvpValueItem is
string uriStr && Uri.TryCreate(uriStr, UriKind.Absolute, out Uri uri))
1826 AddToList(innerList, uri, count++);
1830 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Uri format [{kvpValueItem}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1833 else if (
true == IsGuidType(elementType) ||
true == IsGuidType(innerElementType))
1835 Type gNullableCheck = IsGuidType(innerElementType) ? innerElementType : elementType;
1836 if (kvpValueItem is
string guidNullStr && IsNullableType(gNullableCheck) && guidNullStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1837 AddToList(innerList,
null, count++);
1838 else if (kvpValueItem is
string guidStr && Guid.TryParse(guidStr, out Guid guid))
1839 AddToList(innerList, guid, count++);
1841 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Guid format [{kvpValueItem}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1843 else if (
true == IsTimeSpanType(elementType) ||
true == IsTimeSpanType(innerElementType))
1845 Type tsNullableCheck = IsTimeSpanType(innerElementType) ? innerElementType : elementType;
1846 if (kvpValueItem is
string tsNullStr && IsNullableType(tsNullableCheck) && tsNullStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
1847 AddToList(innerList,
null, count++);
1848 else if (kvpValueItem is
string timeSpanStr && TimeSpan.TryParse(timeSpanStr, out TimeSpan timeSpan))
1849 AddToList(innerList, timeSpan, count++);
1851 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid TimeSpan format [{kvpValueItem}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1854 else if (IsCustomStructType(elementType) || IsCustomStructType(innerElementType))
1856 Type csElemType = IsCustomStructType(innerElementType) ? innerElementType : elementType;
1858 dynamic csItem = kvpValueItem;
1859 if (csItem is IList csl3 && csl3.Count == 1 && csl3[0] is IDictionary csd3)
1861 if (csItem is IDictionary csStructDict3)
1863 element = CreateObjectFromDictionary(csStructDict3, csElemType, parentDictionary, columnNames);
1864 AddToList(innerList, element, count++);
1868 var converter = TypeDescriptor.GetConverter(csElemType);
1869 if (converter !=
null && converter.CanConvertFrom(csItem.GetType()))
1871 element = converter.ConvertFrom(csItem);
1872 AddToList(innerList, element, count++);
1874 else if (
false == IsSimpleType(csItem.GetType()))
1878 AddToList(innerList, csItem, count++);
1882 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported struct type [{csItem.GetType()}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1887 else if (IsSimpleType(elementType) || IsNullableType(elementType))
1889 if (IsSimpleType(kvpValueItem.GetType()) || IsNullableType(kvpValueItem.GetType()))
1891 element = ConvertValue(kvpValueItem, elementType,
null, kvpName);
1892 AddToList(innerList, element, count++);
1896 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Value type [{kvpValueItem.GetType()}] incompatible with simple/nullable element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1899 else if (IsClassType(elementType) && elementType != typeof(
string))
1901 var converter = TypeDescriptor.GetConverter(elementType);
1902 if (converter !=
null && converter.CanConvertFrom(kvpValueItem.GetType()))
1904 element = converter.ConvertFrom(kvpValueItem);
1905 AddToList(innerList, element, count++);
1911 AddToList(innerList, kvpValueItem, count++);
1915 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported class type [{kvpValueItem.GetType()}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1921 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Unsupported item type [{kvpValueItem?.GetType()}] for element type [{elementType}]", kvpValueItem, GPALObjectType.Other);
1925 if (ObjectCopier.GetItemCount(kvpProperty, innerList) > 0)
1927 if (IsEnumerableObject(objOrList) || objOrList.GetType().IsArray)
1928 AddToList(objOrList, innerList, count++);
1929 else if (objOrList.GetType().IsValueType)
1931 string bfName = $
"<{kvpProperty.Name}>k__BackingField";
1932 objOrList = GetSetStructMethod(objOrList.GetType()).Invoke(
null,
new object[] { (object)objOrList, bfName, innerList });
1935 kvpProperty.SetValue(objOrList, innerList);
1939 string currentErrorMessage = $
"No supplied values for [{kvpProperty?.Name}]";
1940 if (
false == lastErrorMessage.Contains(currentErrorMessage))
1942 lastErrorMessage.Add(currentErrorMessage);
1943 GPAL.PublishSimpleEvent(GPALEventType.ERROR, currentErrorMessage, objOrList, GPALObjectType.Other);
1944 supressedMessage =
false;
1946 else if (
false == supressedMessage)
1948 GPAL.PublishSimpleEvent(GPALEventType.INFO,
"Supressing repeat messages", objOrList, GPALObjectType.Other);
1949 supressedMessage =
true;
1953 else if (IsComplexType(elementType))
1955 if (kvpValue !=
null)
1957 itemobj = ParseComplex(kvpValue.ToString());
1958 AddToList(objOrList, itemobj, count++);
1967 SetValue(ref objOrList, kvpProperty, kvpValue, kvpPropertyType, parentDictionary, columnNames);
1970 else if (IsCustomStructType(kvpPropertyType))
1973 IDictionary structKvpDict = kvpValue as IDictionary;
1974 if (structKvpDict ==
null && kvpValue is IList svl && svl.Count == 1 && svl[0] is IDictionary svd)
1975 structKvpDict = svd;
1976 if (structKvpDict !=
null)
1978 object structObj = CreateObjectFromDictionary(structKvpDict, kvpPropertyType, parentDictionary, columnNames);
1979 if (objOrList.GetType().IsValueType)
1981 string bfName = $
"<{kvpProperty.Name}>k__BackingField";
1982 objOrList = GetSetStructMethod(objOrList.GetType()).Invoke(
null,
new object[] { (object)objOrList, bfName, structObj });
1985 kvpProperty.SetValue(objOrList, structObj);
1988 else if (IsComplexType(kvpPropertyType) || IsNullableType(kvpPropertyType) || IsSimpleType(kvpPropertyType))
1990 SetValue(ref objOrList, kvpProperty, kvpValue, kvpPropertyType, parentDictionary, columnNames);
1992 else if (kvpProperty.Name.Equals(kvpName, StringComparison.OrdinalIgnoreCase))
1994 SetValue(ref objOrList, kvpProperty, kvpValue, kvpPropertyType, parentDictionary, columnNames);
1996 else if (kvpValue !=
null && (IsDictionaryType(kvpValue.GetType()) || IsEnumerableType(kvpValue.GetType())))
1998 objOrList = CreateObjectFromDictionary(kvpValue, targetType, kvpValue, columnNames);
2002 noPropertyOrField++;
2005 else if (kvpProperty !=
null && !kvpProperty.CanWrite && kvpProperty.Name.Equals(kvpName, StringComparison.OrdinalIgnoreCase))
2007 if (
false == targetType.Name.StartsWith(
"GPAL"))
2008 GPAL.PublishSimpleEvent(GPALEventType.DEBUG, $
"[{targetType.Name}][{kvpName}] 'CanWrite' [false]", objOrList, GPALObjectType.Other);
2010 else if (kvpField !=
null && kvpField.Name.Equals(kvpName, StringComparison.OrdinalIgnoreCase))
2012 if (IsEnumerableType(kvpFieldType) || kvpFieldType.IsArray)
2014 Type elementType = kvpFieldType.IsArray ? kvpFieldType.GetElementType() : kvpFieldType.GetGenericArguments()[0];
2015 itemobj = ConverterHelper.InstantiateOne(elementType);
2018 if (IsEnumerableObject(kvpValue) || IsArray(kvpValue))
2020 object innerList = ConverterHelper.InstantiateOne(kvpFieldType, ObjectCopier.GetItemCount(kvpProperty, kvpValue));
2021 foreach (dynamic kvpValueItem
in (IEnumerable)kvpValue)
2023 Type kvpValueItemType = kvpValueItem.GetType();
2024 object element =
null;
2026 if (IsKeyValuePairType(kvpValueItemType))
2028 var nestedKvp = kvpValueItem;
2029 Type valueType = elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
2030 ? elementType.GetGenericArguments()[1]
2033 if (IsDictionaryType(elementType))
2035 element = CreateObjectFromDictionary(
new Dictionary<object, object> { { nestedKvp.Key, nestedKvp.Value } }, elementType, kvpValue, columnNames);
2037 else if (IsComplexType(valueType))
2039 element = ParseComplex(nestedKvp.Value?.ToString());
2041 else if (IsSimpleType(valueType))
2043 element = ConvertValue(kvp.Value?.ToString().Trim(
'"'), valueType);
2047 element = nestedKvp.Value;
2049 AddToList(innerList, element, count++);
2051 else if (IsDictionaryType(kvpValueItemType) || IsEnumerableType(elementType) || elementType.IsArray)
2053 element = CreateObjectFromDictionary(kvpValueItem, elementType, kvpValue, columnNames);
2054 AddToList(innerList, element, count++);
2056 else if (IsComplexType(elementType))
2058 element = ParseComplex(kvpValueItem.ToString());
2059 AddToList(innerList, element, count++);
2061 else if (IsSimpleType(elementType))
2063 element = ConvertValue(kvpValueItem.ToString(), elementType);
2064 AddToList(innerList, Convert.ChangeType(element, elementType), count++);
2068 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Skipping unexpected item type [{kvpValueItemType.Name}] for [{elementType.Name}]", kvpValueItem, GPALObjectType.None);
2072 if (ObjectCopier.GetItemCount(kvpProperty, innerList) > 0)
2074 if (IsEnumerableObject(objOrList) || objOrList.GetType().IsArray)
2075 AddToList(objOrList, innerList, count++);
2076 else if (objOrList.GetType().IsValueType)
2077 objOrList = GetSetStructMethod(objOrList.GetType()).Invoke(
null,
new object[] { (object)objOrList, kvpField.Name, (
object)innerList });
2079 kvpField.SetValue(objOrList, innerList);
2083 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $
"No values for [{kvpField.Name}]", objOrList, GPALObjectType.Other);
2086 else if (IsComplexType(kvpFieldType))
2088 itemobj = ParseComplex(kvpValue.ToString());
2089 AddToList(objOrList, itemobj, count++);
2091 else if (IsSimpleType(kvpFieldType))
2093 itemobj = ConvertValue(kvpValue.ToString(), kvpFieldType);
2094 SetValue(ref objOrList, kvpField, itemobj, kvpFieldType, parentDictionary, columnNames);
2098 SetValue(ref objOrList, kvpField, kvpValue, kvpFieldType, parentDictionary, columnNames);
2101 else if (IsCustomStructType(kvpFieldType) && kvpValue is IDictionary)
2103 object structObj = CreateObjectFromDictionary(kvpValue, kvpFieldType, parentDictionary, columnNames);
2104 kvpField.SetValue(objOrList, structObj);
2106 else if (IsComplexType(kvpFieldType) || IsNullableType(kvpFieldType) || IsSimpleType(kvpFieldType))
2108 SetValue(ref objOrList, kvpField, kvpValue, kvpFieldType, parentDictionary, columnNames);
2110 else if (kvpField.Name.Equals(kvpName, StringComparison.OrdinalIgnoreCase))
2112 SetValue(ref objOrList, kvpField, kvpValue, kvpFieldType, parentDictionary, columnNames);
2116 noPropertyOrField++;
2139 if (noPropertyOrField == 2)
2143 string currentErrorMessage = $
"No property/field [{kvpName}] in [{targetType.Name}]";
2144 if (
false == lastErrorMessage.Contains(currentErrorMessage))
2146 lastErrorMessage.Add(currentErrorMessage);
2147 GPAL.PublishSimpleEvent(GPALEventType.WARNING, currentErrorMessage, objOrList, GPALObjectType.Other);
2148 supressedMessage =
false;
2150 else if (
false == supressedMessage)
2152 GPAL.PublishSimpleEvent(GPALEventType.INFO,
"Supressing repeat messages", objOrList, GPALObjectType.Other);
2153 supressedMessage =
true;
2159 if (addedToVisited) _dictVisited.Remove((
object)dictionary);
2160 if (isRoot) _dictVisited =
null;
2163 private static readonly Dictionary<Type, MethodInfo> _setStructMethodCache =
new Dictionary<Type, MethodInfo>();
2164 private static MethodInfo GetSetStructMethod(Type structType)
2166 if (!_setStructMethodCache.TryGetValue(structType, out var m))
2169 .GetMethod(
"SetStructBackingField", BindingFlags.NonPublic | BindingFlags.Static)
2170 .MakeGenericMethod(structType);
2171 _setStructMethodCache[structType] = m;
2180 private static object SetStructBackingField<T>(T instance,
string fieldName,
object value) where T :
struct
2182 FieldInfo field = typeof(T).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance)
2183 ?? typeof(T).GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
2185 field.SetValueDirect(__makeref(instance), value);
2188 private static bool IsList(
object obj)
2190 return obj is IList && obj.GetType().IsGenericType;
2192 private static bool IsArray(
object obj)
2194 return obj !=
null && obj.GetType().IsArray;
2197 static object SetValueDirect(FieldInfo field,
object instance,
object value)
2199 field.SetValueDirect(__makeref(instance), value);
2214 internal static void SetValue(ref dynamic instance, dynamic propertyOrField, dynamic propertyValue, Type propertyType, dynamic parentDictionary, List<string> columnNames)
2219 if (propertyValue is
string recursionMarker && propertyType != typeof(
string)
2220 && recursionMarker.StartsWith(
"Recursion to [") && recursionMarker.EndsWith(
"]"))
2226 if (propertyValue is IList svSingleList && svSingleList.Count == 1
2227 && svSingleList[0] is IDictionary svInnerDict
2228 && !IsEnumerableType(propertyType) && !propertyType.IsArray && !IsDictionaryType(propertyType))
2230 propertyValue = svInnerDict;
2233 dynamic entryValue =
null;
2234 string propertyTypeName = propertyType.Name;
2240 if ((IsEnumerableType(propertyType) || propertyType.IsArray) && !IsDictionaryType(propertyType))
2243 if (propertyValue is IList pvList && pvList.Count == 1 && pvList[0] is IDictionary pvInner && pvInner.Count == 1)
2245 foreach (DictionaryEntry xw
in pvInner)
2247 propertyValue = xw.Value is IList ? xw.Value :
new List<object> { xw.Value };
2252 else if (propertyValue is IDictionary xmlWrapperDict && xmlWrapperDict.Count == 1)
2254 foreach (DictionaryEntry xw
in xmlWrapperDict)
2256 propertyValue = xw.Value is IList ? xw.Value :
new List<object> { xw.Value };
2262 if (typeof(IPAddress) == propertyType)
2264 if (propertyValue is IDictionary ipDict)
2266 foreach (var v
in ipDict.Values) { entryValue = v;
break; }
2269 entryValue = propertyValue?.ToString();
2271 else if (IsTupleType(propertyType) && propertyValue is IDictionary)
2275 object tupleValue = CreateObjectFromDictionary(propertyValue, propertyType, parentDictionary, columnNames);
2276 if (IsValueType(instance.GetType()))
2278 if (propertyOrField is PropertyInfo tupleProp)
2280 string bfName = $
"<{tupleProp.Name}>k__BackingField";
2281 instance = GetSetStructMethod(instance.GetType()).Invoke(
null,
new[] { instance, bfName, tupleValue });
2283 else if (propertyOrField is FieldInfo tupleField)
2284 instance = GetSetStructMethod(instance.GetType()).Invoke(
null,
new[] { instance, tupleField.Name, tupleValue });
2287 propertyOrField.SetValue(instance, tupleValue);
2290 else if (IsComplexType(propertyType) && propertyValue is IEnumerable && !(propertyValue is
string))
2295 propertyOrField.SetValue(instance, ComplexFromDict(propertyValue));
2299 entryValue = ValueToString(propertyValue, propertyType);
2301 if (
true == IsValueType(instance.GetType()))
2306 if (propertyValue is IDictionary svCycleD && svCycleD.Count == 1 && !IsDictionaryType(propertyType))
2308 foreach (DictionaryEntry svce
in svCycleD)
2310 if (svce.Value is
string svceStr && svceStr.StartsWith(
"Recursion to ["))
2312 propertyValue =
null;
2319 if (propertyOrField is FieldInfo)
2324 if (propertyValue is IDictionary && !IsSimpleType(propertyType))
2325 converted = CreateObjectFromDictionary(propertyValue, propertyType,
null,
null);
2327 converted = ConvertValue(entryValue, propertyType);
2328 instance = GetSetStructMethod(instance.GetType()).Invoke(
null,
new[] { instance, ((FieldInfo)propertyOrField).Name, converted });
2336 if (propertyValue is IDictionary && !IsSimpleType(propertyType))
2337 converted = CreateObjectFromDictionary(propertyValue, propertyType,
null,
null);
2339 converted = ConvertValue(entryValue, propertyType);
2340 string backingName = $
"<{propertyOrField.Name}>k__BackingField";
2341 FieldInfo bf = instance.GetType().GetField(backingName, BindingFlags.NonPublic | BindingFlags.Instance);
2345 instance = GetSetStructMethod(instance.GetType()).Invoke(
null,
new[] { instance, backingName, converted });
2347 propertyOrField.SetValue(instance, converted);
2350 else if (
true == IsVersionType(propertyType))
2352 propertyOrField.SetValue(instance, ConvertValue(propertyValue, propertyType));
2354 else if (IsSimpleType(propertyType) || IsEnumType(propertyType))
2356 if (
null != propertyOrField)
2358 if (typeof(
object) == instance.GetType())
2359 instance = $
"{propertyOrField}{ConverterSettings.OutDelimiter}{propertyValue}";
2364 object convertedValue = ConvertValue(entryValue, propertyType);
2365 propertyOrField.SetValue(instance, convertedValue);
2370 propertyOrField.SetValue(instance, propertyValue);
2374 else if (typeof(
string) == instance?.GetType())
2375 instance = propertyValue;
2377 instance = propertyValue;
2379 else if (
true == IsBigIntegerType(propertyType))
2381 BigInteger bigInteger;
2382 if (
true == BigInteger.TryParse(entryValue, out bigInteger))
2383 propertyOrField.SetValue(instance, bigInteger);
2385 else if (
true == IsComplexType(propertyType))
2387 dynamic complex = ParseComplex(entryValue);
2388 if (
null != complex)
2389 propertyOrField.SetValue(instance, complex);
2391 else if (
true == IsNullableType(propertyType))
2393 Type underlyingType = Nullable.GetUnderlyingType(propertyType);
2394 dynamic underlyingValueObject = InstantiateOne(underlyingType);
2395 SetValue(ref instance, propertyOrField, entryValue, underlyingType, parentDictionary, columnNames);
2397 else if (IsDictionaryType(propertyType))
2399 object value = CreateObjectFromDictionary(propertyValue, propertyType, parentDictionary, columnNames);
2400 propertyOrField.SetValue(instance, value);
2402 else if (
true == IsEnumerableType(propertyType) ||
true == propertyType.IsArray)
2404 dynamic enumeration =
null;
2407 if (
null != propertyValue)
2410 if (propertyValue is
string svNullEnumStr && svNullEnumStr.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
2414 else if ((
true == IsEnumerableObject(propertyValue) && !(propertyValue is
string) && !IsDictionaryType(propertyValue.GetType())) ||
true == IsArray(propertyValue))
2415 enumeration = (IEnumerable)propertyValue;
2418 Type _elemType = propertyType.GetElementType()
2419 ?? (propertyType.IsGenericType ? propertyType.GetGenericArguments()[0] :
null);
2420 bool _compatible = _elemType ==
null
2421 || _elemType.IsAssignableFrom(propertyValue.GetType())
2422 || IsSimpleType(_elemType)
2423 || IsDictionaryType(propertyValue.GetType());
2429 object _singleItem = (_elemType !=
null && propertyValue is IDictionary && !IsDictionaryType(_elemType))
2430 ? CreateObjectFromDictionary(propertyValue, _elemType, parentDictionary, columnNames)
2431 : (object)propertyValue;
2432 enumeration = InstantiateOne(propertyType, 1);
2435 if (
true == IsEnumerableObject(enumeration) &&
false == IsArray(enumeration))
2436 AddToList(enumeration, _singleItem, 0);
2437 else if (
true == IsArray(enumeration))
2438 enumeration[0] = _singleItem;
2442 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Scalar [{propertyValue?.GetType()}] is not compatible with collection element type [{_elemType}]. Leaving [{propertyType}] at default.", propertyValue, GPALObjectType.Other);
2443 enumeration = InstantiateOne(propertyType, 0);
2447 if (enumeration ==
null)
2448 enumeration = InstantiateOne(propertyType, 0);
2451 foreach (dynamic item
in enumeration)
2454 dynamic list = InstantiateOne(propertyType, count);
2457 foreach (dynamic item
in enumeration)
2459 if (
false == IsCustomStructType(item?.GetType()) &&
null == item)
2461 Type _elemType = propertyType.GetElementType() ?? (propertyType.IsGenericType ? propertyType.GetGenericArguments()[0] :
null);
2462 bool _canBeNull = _elemType ==
null || !_elemType.IsValueType || IsNullableType(_elemType);
2464 AddToList(list,
null, count++);
2468 dynamic itemValue = item;
2469 dynamic itemKey =
null;
2470 Type itemType = item.GetType();
2471 Type objectElementType = propertyType.GetElementType();
2472 if (
null == objectElementType)
2473 objectElementType = propertyType.GetGenericArguments()[0];
2475 if (
true == IsKeyValuePairType(itemType))
2478 itemValue = item.Value;
2479 itemType = item.Value.GetType();
2482 if (
true == IsComplexType(objectElementType))
2484 dynamic complex = ParseComplex(itemValue.ToString());
2485 if (
null != complex)
2486 AddToList(list, complex, count++);
2488 else if (
true == IsSimpleType(objectElementType) || IsEnumType(objectElementType))
2490 dynamic tempVal = ConvertValue(itemValue.ToString(), objectElementType,
null, propertyOrField?.Name);
2491 AddToList(list, tempVal, count++);
2493 else if (IsEnumerableType(objectElementType))
2495 dynamic myList = InstantiateOne(objectElementType);
2496 Type elementType = objectElementType.GenericTypeArguments[0];
2501 dynamic resolvedItemValue = itemValue;
2502 if (resolvedItemValue is IList rilvSingle && rilvSingle.Count == 1 && rilvSingle[0] is IDictionary rilvInner && rilvInner.Count == 1)
2504 foreach (DictionaryEntry rkv
in rilvInner) { resolvedItemValue = rkv.Value is IList ? rkv.Value :
new List<object> { rkv.Value };
break; }
2506 else if (resolvedItemValue is IDictionary rilvDict && rilvDict.Count == 1)
2508 foreach (DictionaryEntry rkv
in rilvDict) { resolvedItemValue = rkv.Value is IList ? rkv.Value :
new List<object> { rkv.Value };
break; }
2513 if (resolvedItemValue !=
null && !(resolvedItemValue is IEnumerable))
2515 resolvedItemValue =
new List<object> { resolvedItemValue };
2518 if (
true == IsComplexType(elementType))
2522 foreach (
object item2
in (IEnumerable<dynamic>)resolvedItemValue)
2524 dynamic iv2 = item2;
2525 if (iv2 is IList il2 && ((IList)il2).Count == 1) iv2 = ((IList)il2)[0];
2526 complex = ParseComplex(iv2?.ToString());
2527 if (
null != complex)
2528 AddToList(myList, complex, innerCount++);
2531 else if (IsSimpleType(elementType) || IsEnumType(elementType) || IsBigIntegerType(elementType))
2533 foreach (
object item2
in (IEnumerable<dynamic>)resolvedItemValue)
2535 dynamic iv2 = item2;
2536 if (iv2 is IList il2 && ((IList)il2).Count == 1) iv2 = ((IList)il2)[0];
2537 dynamic convItem = IsBigIntegerType(elementType)
2538 ? System.Numerics.BigInteger.Parse(iv2?.ToString())
2540 AddToList(myList, convItem, innerCount++);
2543 list[count++] = myList;
2545 else if (itemValue is IDictionary itemDictValue && !IsDictionaryType(objectElementType))
2548 dynamic converted = CreateObjectFromDictionary(itemDictValue, objectElementType, parentDictionary, columnNames);
2549 AddToList(list, converted, count++);
2553 list[count++] = itemValue;
2557 propertyOrField.SetValue(instance, list);
2560 else if (
true == IsClassType(propertyType))
2562 dynamic value =
null;
2563 Dictionary<dynamic, dynamic> myDict =
new Dictionary<dynamic, dynamic>();
2566 if (
null != propertyValue)
2567 foreach (dynamic item
in propertyValue)
2568 if (
true == IsDictionaryType(item.GetType()))
2571 value = ConvertToClass(
new List<Dictionary<dynamic, dynamic>>() { myDict }, propertyType, parentDictionary, columnNames);
2574 myDict[item.Key] = item.Value;
2577 value = ConvertToClass(
new List<Dictionary<dynamic, dynamic>>() { myDict }, propertyType, parentDictionary, columnNames);
2583 IDictionary catchDict = propertyValue as IDictionary;
2584 if (catchDict ==
null && propertyValue is IList pvCatchList)
2586 foreach (var pvItem
in pvCatchList)
2587 if (pvItem is IDictionary d) { catchDict = d;
break; }
2589 if (catchDict !=
null)
2590 value = ConvertToClass(
new List<Dictionary<dynamic, dynamic>>() { (Dictionary<dynamic, dynamic>)(
object)catchDict }, propertyType, parentDictionary, columnNames);
2594 propertyOrField.SetValue(instance, value);
2598 GPAL.
PublishSimpleEvent(GPALEventType.ERROR, $
"Unknown object type [{propertyType}]", propertyValue, GPALObjectType.Other);
2601 private static bool IsSupportedCollectionType(Type type)
2603 return type.IsArray ||
2604 (type.IsGenericType && (
2605 type.GetGenericTypeDefinition() == typeof(List<>) ||
2606 type.GetGenericTypeDefinition() == typeof(IList<>) ||
2607 type.GetGenericTypeDefinition() == typeof(ICollection<>)
2619 internal static bool IsCustomStructType(Type type)
2624 !type.IsValueType ||
2632 if (IsKnownSystemStruct(type))
2639 if (type.Assembly == typeof(
object).Assembly)
2644 if (
true == IsRegisteredStruct(type))
2653 internal static bool IsRegisteredStruct(Type type)
2658 private static bool IsKnownSystemStruct(Type type)
2661 if (type == typeof(decimal) ||
2662 type == typeof(DateTime) ||
2663 type == typeof(DateTimeOffset) ||
2664 type == typeof(TimeSpan) ||
2665 type == typeof(TimeZoneInfo) ||
2666 type == typeof(Guid) ||
2667 type == typeof(BigInteger) ||
2668 type == typeof(Complex))
2674 string ns = type?.Namespace ??
string.Empty;
2675 if (ns.StartsWith(
"System") || ns.StartsWith(
"Microsoft"))
2680 if (ns ==
"System" &&
true == type?.Name.StartsWith(
"ValueTuple"))
2684 if (
true == type?.IsGenericType && type?.GetGenericTypeDefinition() == typeof(Nullable<>))
2688 if (ns ==
"System.Numerics")
2692 if (type?.Name ==
"Half" && ns ==
"System")
2698 internal static bool IsArrayType(Type type)
2700 return (
bool)type?.IsArray;
2703 internal static bool IsClassType(Type type)
2708 return (
bool)type?.IsClass && (bool)!type?.IsInterface;
2710 internal static bool IsUriType(Type type)
2712 return type == typeof(Uri);
2714 internal static bool IsSimpleType(Type type)
2719 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
2722 return type.IsPrimitive ||
2724 type == typeof(
object) ||
2725 type == typeof(
string) ||
2726 type == typeof(decimal) ||
2727 type == typeof(Uri) ||
2728 type == typeof(System.Net.IPAddress) ||
2729 type == typeof(System.Version) ||
2730 type == typeof(System.Numerics.BigInteger) ||
2731 type == typeof(
bool) ||
2732 type == typeof(Boolean) ||
2733 type == typeof(
byte) ||
2734 type == typeof(sbyte) ||
2735 type == typeof(
char) ||
2736 type == typeof(
short) ||
2737 type == typeof(ushort) ||
2738 type == typeof(
int) ||
2739 type == typeof(uint) ||
2740 type == typeof(
long) ||
2741 type == typeof(ulong) ||
2742 type == typeof(
float) ||
2743 type == typeof(
double) ||
2744 type == typeof(DateTime) ||
2745 type == typeof(DateTimeOffset)
2748 internal static bool IsValueType(Type type)
2750 return type.IsValueType && !type.IsEnum && !type.IsClass && !type.IsInterface;
2754 internal static bool IsDelimitedDataFormat(DataFormat format)
2756 return format == DataFormat.CARET
2757 || format == DataFormat.COLON
2758 || format == DataFormat.CSV
2759 || format == DataFormat.CUSTOM_DELIMITER
2760 || format == DataFormat.DOT
2761 || format == DataFormat.HYPHEN
2762 || format == DataFormat.PIPE
2763 || format == DataFormat.PRN
2764 || format == DataFormat.SEMICOLON
2765 || format == DataFormat.SPACE
2766 || format == DataFormat.TAB;
2772 internal static object ResolveSimpleDictionaryKey(dynamic rawKey, Type keyType)
2774 string rawKeyStr = rawKey as string;
2775 if (rawKeyStr !=
null && rawKeyStr.StartsWith(
"GPALKEY", StringComparison.Ordinal) &&
int.TryParse(rawKeyStr.Substring(7), out
int gpalKeyIdx))
2776 return ConvertValue(gpalKeyIdx, keyType);
2778 if (rawKey !=
null && keyType.IsAssignableFrom(((
object)rawKey).GetType()))
2779 return (
object)rawKey;
2781 return ConvertValue(rawKey?.ToString(), keyType);
2783 internal static bool IsNullableType(Type type)
2785 return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
2787 private static bool IsKeyValuePairType(Type type)
2789 bool retVal =
false;
2792 retVal = ((bool)type?.IsGenericType && type?.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) || type == typeof(DictionaryEntry);
2798 internal static bool IsDictionaryType(Type type)
2803 return typeof(IDictionary).IsAssignableFrom(type);
2805 public static bool IsBigIntegerType(Type type)
2807 return typeof(BigInteger) == type || typeof(BigInteger?) == type;
2812 private static BigInteger AsBigInteger(
object value)
2814 if (value is BigInteger bi)
return bi;
2815 if (value is
string s)
return BigInteger.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
2816 return new BigInteger(Convert.ToInt64(value, CultureInfo.InvariantCulture));
2818 public static bool IsByteType(Type type)
2820 return typeof(Byte) == type || typeof(Byte?) == type;
2822 public static bool IsBooleanType(Type type)
2824 return typeof(Boolean) == type || typeof(Boolean?) == type;
2826 public static bool IsDateTimeType(Type type)
2828 return typeof(DateTime) == type || typeof(DateTime?) == type;
2830 public static bool IsDateTimeOffsetType(Type type)
2832 return typeof(DateTimeOffset) == type || typeof(DateTimeOffset?) == type;
2834 public static bool IsIpAddressType(Type type)
2836 return typeof(IPAddress) == type;
2838 public static bool IsTimeSpanType(Type type)
2840 return typeof(TimeSpan) == type || typeof(TimeSpan?) == type;
2842 private static bool IsTupleType(Type type)
2844 if (type ==
null || !type.IsGenericType)
return false;
2845 var openType = type.GetGenericTypeDefinition();
2849 return openType.Namespace ==
"System" &&
2850 (openType.Name.StartsWith(
"ValueTuple`") || openType.Name.StartsWith(
"Tuple`"));
2853 public static bool IsGuidType(Type type)
2855 return typeof(Guid) == type || typeof(Guid?) == type;
2857 public static bool IsVersionType(Type type)
2859 return typeof(System.Version) == type;
2861 public static bool IsEnumerableType(Type type)
2866 return (type.IsArray || typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(
string));
2876 CountGraphNodesRec(root, limit, ref count);
2880 private static void CountGraphNodesRec(
object node,
int limit, ref
int count)
2882 if (count >= limit ||
null == node)
2889 if (node is IDictionary dict)
2891 foreach (var v
in dict.Values)
2893 CountGraphNodesRec(v, limit, ref count);
2894 if (count >= limit)
return;
2897 else if (node is IEnumerable en && !(node is
string))
2899 foreach (var v
in en)
2901 CountGraphNodesRec(v, limit, ref count);
2902 if (count >= limit)
return;
2907 public static bool IsEnumerableObject(dynamic dynamicObject)
2911 if (
null == dynamicObject)
2918 return (typeof(IEnumerable).IsAssignableFrom(dynamicObject?.GetType()) || dynamicObject?.GetType().IsArray) && typeof(
string) != dynamicObject?.GetType();
2920 private static bool IsEnumType(Type type)
2922 Type underlying = Nullable.GetUnderlyingType(type) ?? type;
2923 return underlying.IsEnum;
2925 internal static bool IsComplexType(Type type)
2927 return type == typeof(System.Numerics.Complex);
2929 private static bool IsCompatibleType(Type targetType, Type valueType)
2931 if (valueType ==
null || targetType ==
null)
return false;
2933 if (targetType.IsAssignableFrom(valueType))
return true;
2935 if (IsSimpleType(targetType) && IsSimpleType(valueType))
return true;
2937 if (IsNullableType(targetType))
2939 var underlyingType = Nullable.GetUnderlyingType(targetType);
2940 return IsSimpleType(underlyingType) && IsSimpleType(valueType);
2943 if (IsComplexType(targetType) && valueType == typeof(
string))
return true;
2945 if (IsDictionaryType(targetType) && typeof(IDictionary).IsAssignableFrom(valueType))
return true;
2947 if (IsEnumerableType(targetType) && typeof(IEnumerable).IsAssignableFrom(valueType))
return true;
2949 if (IsClassType(targetType) && typeof(IDictionary).IsAssignableFrom(valueType))
return true;
2953 internal static DataFormat GetDataFormatFromExtension(
string filename)
2955 switch (Path.GetExtension(filename).ToLowerInvariant().Replace(
".",
""))
2958 return DataFormat.CSV;
2961 return DataFormat.TAB;
2964 return DataFormat.JSON;
2966 return DataFormat.LOG;
2968 return DataFormat.PDF;
2970 return DataFormat.PRN;
2973 return DataFormat.XLSX;
2975 return DataFormat.XML;
2978 return DataFormat.YAML;
2981 return DataFormat.HTML;
2983 return DataFormat.NOTSET;
2987 public static void ConvertDictionaryToXmlElement(
2990 bool emitWrapper =
true,
2991 bool isTopLevel =
true,
2992 HashSet<object> visited =
null)
2996 XmlDocument doc = parent.OwnerDocument;
2999 XmlElement currentParent = parent;
3000 if (!emitWrapper && isTopLevel)
3003 currentParent =
null;
3006 Type objType = obj?.GetType();
3007 bool isDictionary = obj is IDictionary;
3008 bool isEnumerable = IsEnumerableType(objType) && !(obj is string);
3009 bool isSimple = IsSimpleType(objType);
3010 bool isTuple = IsTupleType(objType);
3014 if (objType !=
null && !objType.IsValueType && objType != typeof(
string) && (
object)obj !=
null)
3016 if (!visited.Add((
object)obj))
3018 if (currentParent !=
null)
3020 var bt = objType.Name.IndexOf(
'`');
3021 var cycleName = bt >= 0 ? objType.Name.Substring(0, bt) : objType.Name;
3022 currentParent.InnerText = $
"Recursion to [{cycleName}]";
3032 foreach (var kvp
in dict)
3034 string key = kvp.Key.ToString();
3035 if (key.StartsWith(
"@GPAL_"))
3038 if (currentParent !=
null)
3039 currentParent.SetAttribute(key.Substring(6), ValueToString(kvp.Value));
3042 foreach (var kvp
in dict)
3044 string key = kvp.Key.ToString();
3045 if (key ==
"#text" && currentParent !=
null)
3047 currentParent.InnerText = ValueToString(kvp.Value);
3056 foreach (var kvp
in dict)
3058 string key = kvp.Key.ToString();
3059 if (key.StartsWith(
"@") || key ==
"#text")
continue;
3060 if (key.StartsWith(
"GPALKEY")) key =
"row";
3064 else if (System.Text.RegularExpressions.Regex.IsMatch(key,
@"^GPAL\d+_"))
3065 key = System.Text.RegularExpressions.Regex.Replace(key,
@"^GPAL\d+_",
"");
3067 string elementName = SanitizeXmlElementName(key);
3068 dynamic value = kvp.Value;
3070 XmlElement child = doc.CreateElement(elementName);
3073 if (currentParent ==
null)
3075 currentParent = child;
3076 doc.AppendChild(child);
3080 currentParent.AppendChild(child);
3083 ConvertDictionaryToXmlElement(value, child, emitWrapper,
false, visited);
3086 else if (isEnumerable)
3088 foreach (dynamic item
in obj)
3092 XmlElement child = doc.CreateElement(
"item");
3093 currentParent.AppendChild(child);
3094 ConvertDictionaryToXmlElement(item, child, emitWrapper,
false, visited);
3098 ConvertDictionaryToXmlElement(item, currentParent, emitWrapper,
false, visited);
3104 if (currentParent !=
null)
3106 var fields = objType.GetFields();
3108 foreach (var field
in fields)
3110 dynamic fieldValue = field.GetValue(obj);
3111 XmlElement child = doc.CreateElement(SanitizeXmlElementName(field.Name));
3112 currentParent.AppendChild(child);
3113 ConvertDictionaryToXmlElement(fieldValue, child, emitWrapper:
true, isTopLevel:
false, visited: visited);
3117 else if (isSimple && (objType ==
null || !objType.IsValueType || objType.IsPrimitive || objType.IsEnum
3118 || objType == typeof(DateTime) || objType == typeof(DateTimeOffset) || objType == typeof(decimal)
3119 || IsNullableType(objType) || IsGuidType(objType) || IsTimeSpanType(objType) || IsBigIntegerType(objType)
3120 || IsComplexType(objType)))
3122 if (currentParent !=
null)
3123 currentParent.InnerText = ValueToString(obj);
3125 else if ((
object)obj !=
null && currentParent !=
null)
3128 foreach (var prop
in objType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
3132 dynamic val = prop.GetValue(obj);
3133 XmlElement child = doc.CreateElement(SanitizeXmlElementName(prop.Name));
3134 currentParent.AppendChild(child);
3135 ConvertDictionaryToXmlElement(val, child, emitWrapper,
false, visited);
3139 foreach (var field
in objType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
3143 dynamic val = field.GetValue(obj);
3144 XmlElement child = doc.CreateElement(SanitizeXmlElementName(field.Name));
3145 currentParent.AppendChild(child);
3146 ConvertDictionaryToXmlElement(val, child, emitWrapper,
false, visited);
3152 public static void ConvertHtmlDictionaryToXml(dynamic obj, XmlElement parent)
3157 XmlDocument doc = parent.OwnerDocument;
3160 if (obj is IEnumerable enumerable && !(obj is
string) && !(obj is IDictionary))
3162 foreach (dynamic child
in enumerable)
3164 ConvertHtmlDictionaryToXml(child, parent);
3170 if (obj is
string text)
3172 parent.AppendChild(doc.CreateTextNode(text));
3177 if (
true == IsDictionaryType(obj.GetType()))
3180 if (obj.TryGetValue(
"tag", out tagObj) &&
"#comment".Equals(tagObj?.ToString()))
3182 string commentText =
"";
3184 if (obj.TryGetValue(
"value", out valueObj))
3186 commentText = SanitizeComment(ValueToString(valueObj));
3190 parent.AppendChild(doc.CreateComment(commentText));
3196 if (!(obj is IDictionary elementDict))
3198 parent.AppendChild(doc.CreateTextNode(ValueToString(obj)));
3202 string tagName =
"div";
3203 dynamic attributes =
null;
3204 dynamic children =
null;
3205 string textContent =
null;
3207 foreach (DictionaryEntry entry
in elementDict)
3209 string key = entry.Key.ToString().ToLower();
3211 tagName = entry.Value?.ToString() ??
"div";
3212 else if (key ==
"attributes")
3213 attributes = entry.Value;
3214 else if (key ==
"children")
3215 children = entry.Value;
3216 else if (key ==
"text")
3217 textContent = entry.Value?.ToString();
3220 XmlElement element = doc.CreateElement(SanitizeXmlElementName(tagName));
3223 if (attributes is IDictionary attrDict)
3225 foreach (DictionaryEntry attr
in attrDict)
3227 string attrName = attr.Key.ToString();
3228 string attrValue = ValueToString(attr.Value);
3229 element.SetAttribute(attrName, attrValue);
3234 if (!
string.IsNullOrEmpty(textContent))
3236 element.AppendChild(doc.CreateTextNode(textContent));
3240 if (children !=
null)
3242 ConvertHtmlDictionaryToXml(children, element);
3245 parent.AppendChild(element);
3249 private static string ValueToString(dynamic value, Type type =
null)
3252 type = value?.GetType();
3255 if (IsKeyValuePairType(type))
3256 return ValueToString(value.Value);
3258 if (
false == IsCustomStructType(type))
3261 if (
null == value || value is
char c && c ==
'\0')
3271 if (value is
string alreadyString)
3272 return alreadyString;
3277 if (value is IList scalarList && type !=
null && !IsEnumerableType(type) && !type.IsArray)
3279 object first = scalarList.Count > 0 ? scalarList[0] :
null;
3280 if (first ==
null)
return "null";
3281 if (first is
string fs)
return fs;
3283 if (first is IDictionary fd)
3285 foreach (DictionaryEntry de
in fd)
3286 if (de.Value is
string dvs)
return dvs;
3290 if (first is IList)
return ValueToString(first, type);
3291 return first.ToString();
3294 if (IsTupleType(type))
3296 var fields = type.GetFields();
3297 var parts =
new List<string>();
3298 foreach (var field
in fields)
3300 var fieldValue = field.GetValue(value);
3301 parts.Add(ValueToString(fieldValue));
3303 return $
"({string.Join(",
", parts)})";
3307 if (IsComplexType(type))
3309 double im = value.Imaginary;
3310 string sign = im >= 0 ?
"+" :
"-";
3311 return $
"{value.Real}{sign}{Math.Abs(im)}i";
3315 if (IsEnumType(type))
3317 object realValue = value;
3320 if (value is
string strValue)
3323 var tryParseMethod = typeof(Enum).GetMethod(nameof(Enum.TryParse),
new[]
3327 type.MakeByRefType()
3330 if (tryParseMethod !=
null)
3332 var parameters =
new object[] { strValue,
true,
null };
3333 bool success = (bool)tryParseMethod.Invoke(
null, parameters);
3337 realValue = parameters[2];
3347 dynamic enumObject = InstantiateOne(type);
3349 return enumObject.ToString();
3362 long numeric = Convert.ToInt64(realValue);
3367 string name = Enum.GetName(type, realValue);
3372 if (type.IsDefined(typeof(FlagsAttribute), inherit:
false))
3374 var names = Enum.GetValues(type)
3376 .Where(v => Convert.ToInt64(v) != 0 && (numeric & Convert.ToInt64(v)) == Convert.ToInt64(v))
3377 .Select(v => Enum.GetName(type, v))
3378 .Where(n => n !=
null);
3381 return string.Join(
", ", names);
3385 return numeric.ToString(CultureInfo.InvariantCulture);
3389 if (IsDateTimeType(type))
3391 return value.ToString(
"MM/dd/yyyy h:mm:ss tt");
3395 if (IsGuidType(type))
3397 return value.ToString();
3401 if (IsUriType(type))
3403 return value.ToString();
3407 if (IsIpAddressType(type))
3409 return value.ToString();
3413 if (IsTimeSpanType(type))
3415 return value.ToString(
"c");
3419 if (IsVersionType(type))
3421 return value.ToString();
3425 if (IsBigIntegerType(type))
3427 return Convert.ToString(value, CultureInfo.InvariantCulture);
3431 if (type == typeof(
float) || type == typeof(
double))
3433 if (
double.IsNaN((
double)value))
3435 if (
double.IsPositiveInfinity((
double)value))
3437 if (
double.IsNegativeInfinity((
double)value))
3440 return Convert.ToString(value, CultureInfo.InvariantCulture);
3443 if (typeof(WaitTime) == type)
3446 if (
true == TryConvertToWaitTime(value, out wt))
3447 return wt.ToString();
3449 wt = CreateObjectFromDictionary(value, type,
null,
null);
3450 return wt.ToString();
3454 if (IsSimpleType(type))
3456 return Convert.ToString(value, CultureInfo.InvariantCulture);
3459 var toString = GetToString(type);
3461 return (
string)toString.Invoke(value,
null);
3463 private static MethodInfo GetToString(Type t)
3465 if (!ToStringCache.TryGetValue(t, out var method))
3467 method = t.GetMethod(
3469 BindingFlags.Instance | BindingFlags.Public,
3471 types: Type.EmptyTypes,
3474 ToStringCache[t] = method;
3480 private static string SanitizeComment(
string text)
3482 if (
string.IsNullOrEmpty(text))
3485 text = text.Replace(
"<!--",
"").Replace(
"-->",
"").Replace(
"--",
"-");
3488 if (text.EndsWith(
"-"))
3494 private static string SanitizeXmlElementName(
string key)
3496 if (
string.IsNullOrEmpty(key))
3499 if (key.StartsWith(
"GPALKEY", StringComparison.Ordinal))
3500 key = key.Substring(7);
3501 else if (System.Text.RegularExpressions.Regex.IsMatch(key,
@"^GPAL\d+_"))
3502 key = System.Text.RegularExpressions.Regex.Replace(key,
@"^GPAL\d+_",
"");
3505 string sanitized = (key.Length > 0 && !
char.IsLetter(key[0]) && key[0] !=
'_')
3510 sanitized = sanitized.Replace(
":",
"_");
3513 sanitized = Regex.Replace(sanitized,
"[^a-zA-Z0-9_\\-.]",
"_");
3516 if (
string.IsNullOrEmpty(sanitized))
3521 internal static string ConvertInputDictionaryToDelimitedAndGrid(
ConverterSettings converterSettings, out IGPALGrid<string> outGrid)
3523 StringBuilder sb =
new StringBuilder();
3524 StringBuilder sb2 =
new StringBuilder();
3525 IGPALGrid<string> outputGrid =
GPAL.
Grid.ToGPALObject();
3526 List<string> gridRow =
new List<string>();
3527 List<Dictionary<object, dynamic>> rows =
new List<Dictionary<object, dynamic>>();
3528 List<string> flattenLastMessage =
new List<string>();
3529 bool flattenSupressedMessage =
false;
3533 IEnumerable<object> rootItems = cleanedInput as IEnumerable<object> ??
new[] { cleanedInput };
3540 bool LooksLikeRecordList(dynamic value) =>
3541 value is IDictionary asDict && asDict.Count > 0 &&
3542 asDict.Keys.Cast<
object>().All(k =>
int.TryParse(k?.ToString(), out _));
3544 void EmitRows(dynamic value)
3546 if (LooksLikeRecordList(value))
3548 foreach (dynamic subValue
in ((IDictionary)value).Values)
3555 if (
false == IsCustomStructType(value?.GetType()) && (value ==
null || (value is ICollection emptyCheck && emptyCheck.Count == 0)))
3558 flattenLastMessage.Clear();
3559 flattenSupressedMessage =
false;
3560 var row =
new Dictionary<object, dynamic>();
3566 if (
false == IsCustomStructType(value?.GetType()) &&
null != value &&
true == IsSimpleType(value.GetType()))
3567 row[
"0"] = value.ToString();
3569 Flatten(value, ref row);
3574 foreach (dynamic item
in rootItems)
3579 List<string> headers =
null;
3583 bool useUserSuppliedHeaders = 0 < ((IGPALFileInternal)converterSettings.OutputFile)?.FileSettings.ColumnList.Count() && 0 < ((IGPALFileInternal)converterSettings.OutputFile)?.FileSettings.ColumnList[0].Count();
3587 List<string> canonicalFieldNames =
null;
3590 canonicalFieldNames =
new List<string>();
3591 foreach (var row
in rows)
3594 foreach (
string key
in row.Keys.Cast<
string>())
3596 int existingIndex = canonicalFieldNames.IndexOf(key);
3597 if (existingIndex >= 0)
3599 lastIndex = existingIndex;
3603 canonicalFieldNames.Insert(lastIndex + 1, key);
3609 if (useUserSuppliedHeaders)
3610 headers = ((IGPALFileInternal)converterSettings.OutputFile).FileSettings.ColumnList[0].ToList();
3612 headers = canonicalFieldNames;
3614 if (
true == converterSettings.FirstLineIsColumnHeaders &&
null != headers)
3616 foreach (var header
in headers)
3618 sb2.Append(CreateCSVToken(header.ToString(), converterSettings));
3619 gridRow.Add(header.ToString());
3621 if (sb2.Length > 0) sb2.Length--;
3622 sb.AppendLine(sb2.ToString());
3624 outputGrid.AddRow(gridRow);
3628 int headerCount = headers?.Count ?? 0;
3630 for (
int rowIndex = startIndex; rowIndex < rows.Count; rowIndex++)
3632 var dict = rows[rowIndex];
3633 gridRow =
new List<string>();
3635 for (
int i = 0; i < headerCount; i++)
3637 string key = useUserSuppliedHeaders ? (i < canonicalFieldNames.Count ? canonicalFieldNames[i] :
null) : headers[i];
3638 string value = key !=
null && dict.ContainsKey(key) && dict[key] !=
null ? dict[key].ToString() :
"";
3639 sb2.Append(CreateCSVToken(value, converterSettings));
3642 char delim =
true == converterSettings.OutDelimiter.HasValue ? converterSettings.OutDelimiter.Value :
',';
3644 if (sb2.Length > 0 && sb2[sb2.Length - 1] == delim) sb2.Length--;
3645 sb.AppendLine(sb2.ToString());
3647 outputGrid.AddRow(gridRow);
3650 void Flatten(dynamic dictionaryOrList, ref Dictionary<object, dynamic> outDict,
string prefix =
"")
3652 StringBuilder sb3 =
new StringBuilder();
3653 Dictionary<object, dynamic> outDict2 =
new Dictionary<object, dynamic>();
3654 Type dictOrListType = dictionaryOrList?.GetType();
3656 if (
true == IsDictionaryType(dictOrListType))
3658 foreach (dynamic kvp
in dictionaryOrList)
3660 Type kvpType = kvp.GetType();
3661 dynamic kvpValue = kvp;
3662 dynamic kvpKey =
null;
3664 if (
true == IsKeyValuePairType(kvpType))
3666 kvpValue = kvp.Value;
3670 Type kvpValueType = kvpValue?.GetType();
3672 if (
true == IsComplexType(kvpValueType))
3674 Complex complex = (Complex)kvpValue;
3676 sb3.Append($
"{CreateCSVToken(complex.Real.ToString(), converterSettings)}{CreateCSVToken(complex.Imaginary.ToString(), converterSettings)}");
3678 sb3.Remove(sb3.Length - 1, 1);
3680 outDict[prefix + kvpKey] = sb3.ToString();
3683 else if (
true == IsSimpleType(kvpValueType))
3685 if (outDict.TryGetValue(prefix + kvpKey, out dynamic val))
3686 sb3.Append($
"{val}");
3688 sb3.Append(ValueToString(kvpValue));
3690 outDict[prefix + kvpKey] = sb3.ToString();
3693 else if (
true == IsIpAddressType(kvpValueType))
3695 IPAddress iPAddress = (IPAddress)kvpValue;
3696 sb3.Append(iPAddress.ToString());
3698 outDict[prefix + kvpKey] = sb3.ToString();
3701 else if (IsBigIntegerType(kvpValueType) || IsVersionType(kvpValueType))
3703 sb3.Append(kvpValue.ToString());
3705 outDict[prefix + kvpKey] = sb3.ToString();
3708 else if (
true == IsDictionaryType(kvpValueType))
3710 Flatten(kvpValue, ref outDict2, prefix + kvpKey +
".");
3711 foreach (var item
in outDict2)
3713 outDict[item.Key] = item.Value;
3717 else if ((
true == IsEnumerableType(kvpValueType) ||
true == kvpValueType.IsArray))
3720 foreach (var item
in kvpValue)
3722 Flatten(item, ref outDict2, $
"{prefix}{kvpKey}{index++}.");
3723 foreach (var item2
in outDict2)
3725 outDict[item2.Key] = item2.Value;
3730 else if ((
true == IsClassType(kvpValueType)) && kvpValueType != typeof(
string))
3732 foreach (var prop
in kvpValueType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
3736 object propValue = prop.GetValue(kvpValue);
3737 if (propValue !=
null)
3739 Flatten(propValue, ref outDict2, $
"{prefix}{kvpKey}.{prop.Name}.");
3740 foreach (var item
in outDict2)
3742 outDict[item.Key] = item.Value;
3746 else if (
true == IsSimpleType(prop.PropertyType))
3752 outDict[$
"{prefix}{kvpKey}.{prop.Name}."] =
"";
3755 catch (Exception ex)
3757 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to flatten property [{prop.Name}] of [{kvpValueType.Name}]", kvp, GPALObjectType.Other, ex);
3763 GPAL.
PublishSimpleEvent(GPALEventType.DEBUG, $
"Unable to handle type [{kvpValueType}]", kvp, GPALObjectType.Other);
3769 if (
true == IsKeyValuePairType(dictOrListType))
3771 if (
true == IsDictionaryType(dictionaryOrList.Value.GetType()))
3773 Flatten(dictionaryOrList.Value, ref outDict2, $
"{prefix}");
3774 foreach (var item2
in outDict2)
3776 outDict[item2.Key] = item2.Value.Trim(
true == converterSettings.InDelimiter.HasValue ? converterSettings.InDelimiter.Value :
',');
3781 outDict[dictionaryOrList.Key] = dictionaryOrList.Value;
3784 else if (
false == IsCustomStructType(dictOrListType) &&
true == ValueToString(dictionaryOrList).Equals(
"null"))
3787 outDict[prefix] =
null;
3789 else if (
true == IsComplexType(dictOrListType))
3791 Complex complex = (Complex)dictionaryOrList;
3793 sb3.Append($
"{complex.Real.ToString()}{complex.Imaginary.ToString()}");
3795 sb3.Remove(sb3.Length - 1, 1);
3797 outDict[prefix] = sb3.ToString();
3799 else if (
true == IsSimpleType(dictOrListType))
3801 if (outDict.TryGetValue(prefix, out dynamic val))
3802 sb3.Append($
"{val}");
3804 sb3.Append(dictionaryOrList.ToString());
3806 outDict[prefix] = sb3.ToString();
3808 else if (
true == IsIpAddressType(dictOrListType))
3810 IPAddress iPAddress = (IPAddress)dictionaryOrList;
3811 sb3.Append(iPAddress.ToString());
3813 outDict[prefix] = sb3.ToString();
3815 else if (IsBigIntegerType(dictOrListType) || IsVersionType(dictOrListType))
3817 sb3.Append(dictionaryOrList.ToString());
3819 outDict[prefix] = sb3.ToString();
3821 else if (
true == IsEnumerableType(dictOrListType) ||
true == IsArrayType(dictOrListType))
3824 foreach (var item
in dictionaryOrList)
3826 Flatten(item, ref outDict2, $
"{prefix}{index++}");
3827 foreach (var item2
in outDict2)
3829 outDict[item2.Key] = item2.Value;
3836 string flattenMsg = $
"Unable to handle type [{dictOrListType}]";
3837 if (
false == flattenLastMessage.Contains(flattenMsg))
3839 flattenLastMessage.Add(flattenMsg);
3841 flattenSupressedMessage =
false;
3843 else if (
false == flattenSupressedMessage)
3845 GPAL.
PublishSimpleEvent(GPALEventType.INFO,
"Supressing repeat messages", dictionaryOrList, GPALObjectType.Other);
3846 flattenSupressedMessage =
true;
3852 outGrid = outputGrid;
3853 return sb.ToString();
3855 internal static string CreateCSVToken(
string inputString,
ConverterSettings converterSettings)
3857 char delimiter =
true == converterSettings.OutDelimiter.HasValue ? converterSettings.OutDelimiter.Value :
',';
3858 inputString = (inputString ??
"").TrimEnd(delimiter);
3860 StringBuilder sb =
new StringBuilder();
3861 if (
true == converterSettings.FieldsEnclosedInQuotes)
3864 sb.Append(inputString);
3866 if (
true == converterSettings.FieldsEnclosedInQuotes)
3869 if (0 == sb.Length || delimiter != sb[sb.Length - 1])
3870 sb.Append(delimiter);
3872 return sb.ToString();
3874 internal static Dictionary<object, dynamic> ConvertXMLToDictionary(XmlDocument xmlDoc)
3877 var rootDict =
new Dictionary<object, dynamic>();
3880 string rootName = xmlDoc.DocumentElement.Name;
3883 dynamic contentDict = ConvertXmlNodeToDictionary(xmlDoc.DocumentElement);
3886 rootDict[rootName] = contentDict;
3891 private static int xmlItemCount = 0;
3892 private static Dictionary<object, dynamic> ConvertXmlNodeToDictionary(XmlNode xmlNode)
3894 var dict =
new Dictionary<object, dynamic>();
3899 if (xmlNode.Attributes !=
null)
3901 foreach (XmlAttribute attr
in xmlNode.Attributes)
3903 dict[
"@GPAL_" + attr.Name] = attr.Value;
3908 var childGroups =
new Dictionary<string, List<Dictionary<object, dynamic>>>();
3910 string textContent =
null;
3911 string rawWhitespaceText =
null;
3913 foreach (XmlNode childNode
in xmlNode.ChildNodes)
3915 if (childNode.NodeType == XmlNodeType.Element)
3917 var childDict = ConvertXmlNodeToDictionary(childNode);
3918 string name = $
"GPAL{xmlItemCount++.ToString("D3
")}_"+childNode.Name;
3920 if (!childGroups.TryGetValue(name, out var list))
3922 list =
new List<Dictionary<object, dynamic>>();
3923 childGroups[name] = list;
3925 list.Add(childDict);
3927 else if (childNode.NodeType == XmlNodeType.Text
3928 || childNode.NodeType == XmlNodeType.Whitespace
3929 || childNode.NodeType == XmlNodeType.SignificantWhitespace)
3931 string raw = childNode.Value ??
string.Empty;
3932 string trimmed = raw.Trim();
3933 if (!
string.IsNullOrEmpty(trimmed))
3934 textContent = trimmed;
3935 else if (raw.Length > 0)
3936 rawWhitespaceText = raw;
3941 if (textContent ==
null && rawWhitespaceText !=
null && childGroups.Count == 0)
3942 textContent = rawWhitespaceText;
3945 foreach (var group
in childGroups)
3950 dict[group.Key] = group.Value;
3954 if (textContent !=
null)
3956 dict[
"#text"] = textContent;
3959 catch (Exception ex)
3961 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Unable to convert XMLnode [{xmlNode}] to dictionary.",
null, GPALObjectType.None, ex);
3966 internal static List<Dictionary<object, dynamic>> ConvertGridToDictionary(List<string> columnNames, IGPALGrid<string> rows,
int startRow = 0,
int rowCnt = -1)
3968 List<Dictionary<object, dynamic>> returnList =
new List<Dictionary<object, dynamic>>();
3969 var rowDictionary =
new Dictionary<object, dynamic>();
3973 if (0 == startRow && -1 == rowCnt)
3976 foreach (List<string> row
in rows)
3978 rowDictionary =
new Dictionary<object, dynamic>();
3981 foreach (
string columnName
in columnNames)
3982 rowDictionary[columnName] = row[idx++];
3984 returnList.Add(rowDictionary);
3987 catch (Exception ex)
3989 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Unable to convert grid to dictionary.", rows, GPALObjectType.Other, ex);
3995 for (
int rowIdx = startRow; ourRowCnt < rowCnt; ourRowCnt++)
3997 rowDictionary =
new Dictionary<object, dynamic>();
3998 List<string> row = rows[rowIdx++];
4001 foreach (
string columnName
in columnNames)
4002 rowDictionary[columnName] = row[idx++];
4004 returnList.Add(rowDictionary);
4007 catch (Exception ex)
4009 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Unable to convert grid to dictionary.", rows, GPALObjectType.Other, ex);
4028 internal static Dictionary<object, dynamic> ConvertDelimitedToDictionary(
4031 string filename =
null)
4033 var records =
new Dictionary<object, dynamic>();
4034 List<string> headers =
null;
4038 converterSettings?.InDelimiter ??
4039 GetDelimiterFromFormat(
4040 GetDataFormatFromExtension(filename ??
@"csv")
4047 if (inputFile is Stream stream)
4049 stream.Position = 0;
4050 reader =
new StreamReader(stream);
4052 else if (inputFile is StreamReader sr)
4056 else if (inputFile is StringReader str)
4063 GPALEventType.ERROR,
4064 $@"Unsupported inputFile type [{inputFile?.GetType().FullName}]",
4066 GPALObjectType.GPALFile);
4071 while ((line = reader.ReadLine()) !=
null)
4073 if (
string.IsNullOrWhiteSpace(line))
4079 var tokens = SplitRespectingQuotes(line, delimiter);
4082 if (lineNumber == 0)
4084 if (converterSettings?.FirstLineIsColumnHeaders ==
true)
4094 if (headers !=
null)
4097 var rowDict =
new Dictionary<string, string>();
4098 for (
int i = 0; i < Math.Min(headers.Count, tokens.Count); i++)
4100 rowDict[headers[i]] = tokens[i];
4102 records[$
"GPALKEY{lineNumber:D4}"] = rowDict;
4107 records[$
"GPALKEY{lineNumber:D4}"] = tokens;
4113 catch (Exception ex)
4116 GPALEventType.EXCEPTION,
4117 $@"Exception while parsing delimited file",
4119 GPALObjectType.GPALFile, ex);
4122 if (records.Count == 0)
4126 $@"No data found in input file [{filename}]",
4128 GPALObjectType.GPALFile);
4140 private static IEnumerable<string> ReadAllLines(TextReader reader)
4142 var lines =
new List<string>();
4146 while (
null != (line = reader.ReadLine()))
4153 internal static char? GetDelimiterFromFormat(DataFormat dataFormat)
4157 case DataFormat.CARET:
4159 case DataFormat.COLON:
4161 case DataFormat.CSV:
4163 case DataFormat.DOT:
4165 case DataFormat.HYPHEN:
4167 case DataFormat.PIPE:
4169 case DataFormat.PRN:
4170 case DataFormat.SPACE:
4172 case DataFormat.SEMICOLON:
4174 case DataFormat.TAB:
4177 case DataFormat.HTML:
4178 case DataFormat.JSON:
4179 case DataFormat.PDF:
4180 case DataFormat.XLSX:
4181 case DataFormat.XML:
4182 case DataFormat.YAML:
4185 case DataFormat.CUSTOM_DELIMITER:
4195 internal static bool IsArrayLike(dynamic dictListOrObject)
4197 Type parmType = dictListOrObject.GetType();
4199 if (IsDictionaryType(parmType) || IsEnumerableType(parmType))
4201 if (dictListOrObject ==
null || dictListOrObject.Count == 0)
return false;
4203 foreach (dynamic entry
in dictListOrObject)
4207 string keyStr = entry.Key.ToString();
4208 if (!keyStr.StartsWith(
"GPALKEY"))
return false;
4209 if (
int.TryParse(keyStr.Substring(7), out
int idx))
4227 private static bool TryGetIdAndRest(IDictionary dict, out
string id, out Dictionary<object, dynamic> rest)
4230 rest =
new Dictionary<object, dynamic>();
4232 if (dict.Contains(
"id"))
4234 dynamic idValue = dict[
"id"];
4235 if (idValue is
string idStr)
4238 foreach (DictionaryEntry kvp
in dict)
4240 if (!
"id".Equals(kvp.Key.ToString()))
4242 rest[kvp.Key] = kvp.Value;
4250 foreach (DictionaryEntry kvp
in dict)
4252 rest[kvp.Key] = kvp.Value;
4258 private static string RenderFullObjectToHtml(IDictionary dict,
bool emitUl =
false,
bool isTopLevel =
true,
string listStyle =
"style='margin: 8px; padding-left: 8px;'")
4260 var sb =
new StringBuilder();
4261 int dictCount = dict.Count;
4263 if (
true == isTopLevel)
4264 sb.Append($
"<ul id='renderfull' {listStyle}>\n");
4266 foreach (
object keyObj
in dict.Keys)
4268 string key = keyObj.ToString().Replace(
"@GPAL_",
"");
4270 key = System.Text.RegularExpressions.Regex.Replace(key,
@"^GPAL\d+_",
"");
4275 if (
true == System.Text.RegularExpressions.Regex.IsMatch(key,
@"^GPALKEY\d+$"))
4278 if (
"#comment".Equals(key))
continue;
4279 dynamic value = dict[keyObj];
4281 if (
false == key.Equals(
"#text") &&
false ==
string.IsNullOrEmpty(key))
4283 sb.Append(
"<li><strong>");
4284 sb.Append(HttpUtility.HtmlEncode(key));
4285 sb.Append(
"</strong>: ");
4286 listStyle =
"style = 'list-style: none; margin: 8px; padding-left: 8px;'";
4288 Type valueType = value?.GetType();
4289 bool isCollection = IsDictionaryType(valueType) || IsEnumerableType(valueType);
4290 bool isTuple = IsTupleType(valueType);
4292 int collectionCount = 0;
4294 if (
true == isCollection)
4295 collectionCount = ObjectCopier.GetItemCount(
null, value);
4297 if (
true == emitUl && 1 < dictCount)
4298 sb.Append($
"<ul id=rf2 {listStyle}><li>\n");
4300 sb.Append(RenderValue(value, isCollection || isTuple, isCollection || isTuple ?
"style='margin: 8px; padding-left: 8px;'" : listStyle));
4302 if (
true == emitUl && 1 < dictCount)
4303 sb.Append(
"</ul></li>");
4305 if (
false == key.Equals(
"#text"))
4306 sb.Append(
"</li>\n");
4309 if (
true == isTopLevel)
4310 sb.Append(
"</ul>\n");
4312 return sb.ToString();
4315 private static int idCnt = 0;
4318 private static string RenderValue(dynamic value,
bool renderUl =
false,
string ulStyle =
"style='margin: 0px; padding-left: 0px;", HashSet<object> visited =
null)
4320 var valueSb =
new StringBuilder();
4321 Type valueType = value?.GetType();
4322 string vtn = valueType?.Name;
4325 bool isRootCall = visited ==
null;
4326 bool isNull =
false;
4335 isNull =
null == value;
4342 Dictionary<object, dynamic> dict =
new Dictionary<object, dynamic>();
4344 if (
false == isNull)
4348 if (valueType.IsClass || valueType.IsInterface)
4350 if (visited.TryGetValue(value, out dynamic fuggetAboutIt))
4352 string referenceName =
string.Empty;
4356 referenceName = value.Value;
4362 referenceName = value.Name;
4366 referenceName = valueType.Name;
4370 return $
"Recursion to [{referenceName}]";
4378 if (
true == IsKeyValuePairType(valueType))
4380 return $
"<strong>{value.Key}</strong>: {RenderValue(value.Value, true, ulStyle, visited)}";
4383 else if (IsTupleType(valueType))
4385 if (
true == renderUl)
4386 valueSb.Append($
"<ul id=rvtuple{idCnt++} {ulStyle}'>\n");
4389 var fields = valueType.GetFields();
4390 var parts =
new List<string>();
4391 foreach (dynamic field
in fields)
4393 var fieldValue = field.GetValue(value);
4394 valueSb.AppendLine($
"<li><strong>{field.Name}</strong>: {RenderValue(fieldValue, true, ulStyle, visited)}</li>");
4397 if (
true == renderUl)
4398 valueSb.Append(
"</ul>\n");
4400 else if (
false == IsCustomStructType(valueType) && value ==
null)
4401 return HttpUtility.HtmlEncode(
"null");
4403 else if (value is Dictionary<object, dynamic> nestedDict)
4405 valueSb.Append(RenderFullObjectToHtml(nestedDict, renderUl,
false));
4408 else if (
true == IsEnumerableObject(value) && !(value is
string))
4410 if (
true == renderUl)
4411 valueSb.Append($
"<ul id=rvenum{idCnt++} {ulStyle}'>\n");
4413 foreach (var item
in value)
4415 Type itemType = item?.GetType();
4416 bool isCollection = IsDictionaryType(itemType) || IsEnumerableType(itemType);
4417 string toRender = RenderValue(item, isCollection || renderUl, ulStyle, visited);
4419 if (
false ==
string.IsNullOrEmpty(toRender))
4423 if ((
false == isCollection ||
true == renderUl) &&
false == toRender.StartsWith(
"<li>"))
4424 valueSb.Append(
"<li>");
4426 valueSb.Append(toRender);
4428 if ((
false == isCollection ||
true == renderUl) &&
false == toRender.StartsWith(
"<li>"))
4429 valueSb.Append(
"</li>\n");
4433 if (
true == renderUl)
4434 valueSb.Append(
"</ul>\n");
4438 else if (
true == IsSimpleType(valueType) &&
false == IsCustomStructType(valueType))
4440 string toRender = ValueToString(value);
4441 if (
false ==
string.IsNullOrEmpty(toRender))
4442 valueSb.Append($
"{HttpUtility.HtmlEncode(toRender)}");
4444 else if (
true == IsCustomStructType(valueType) ||
true == IsClassType(valueType))
4446 if (
true == renderUl)
4447 valueSb.Append($
"<ul id=rvstruct{idCnt++} {ulStyle}'>\n");
4449 var fields = valueType.GetFields(BindingFlags.Public | BindingFlags.Instance);
4450 var props = valueType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
4452 foreach (var field
in fields)
4454 dynamic fieldValue = field?.GetValue(value);
4455 valueSb.Append($
"<li><strong>{field?.Name}</strong>: ");
4456 Type fieldValueType = fieldValue?.GetType();
4458 if (
true == IsEnumerableType(fieldValueType))
4459 valueSb.Append(RenderValue(fieldValue,
true, ulStyle, visited));
4461 valueSb.Append(ValueToString(fieldValue));
4463 valueSb.Append(
"</li>\n");
4466 foreach (var prop
in props)
4468 if (
true == prop?.CanRead && prop?.GetIndexParameters().Length == 0)
4470 dynamic propValue = prop?.GetValue(value);
4471 valueSb.Append($
"<li><strong>{prop?.Name}</strong>: ");
4472 Type propValueType = propValue?.GetType();
4473 bool isEnumerable = IsEnumerableType(propValueType);
4474 bool isClass = IsClassType(propValueType);
4475 bool isSimple = IsSimpleType(propValueType);
4477 if (
true == isEnumerable || (
true == isClass &&
false == isSimple))
4478 valueSb.Append(RenderValue(propValue,
true, ulStyle, visited));
4480 valueSb.Append(ValueToString(propValue));
4482 valueSb.Append(
"</li>\n");
4486 if (
true == renderUl)
4487 valueSb.Append(
"</ul>\n");
4491 valueSb.Append(ValueToString(value));
4492 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Render [{valueType}] using ValueToString", value, GPALObjectType.Other);
4495 return valueSb.ToString();
4499 public static string ConvertDictionaryToHtml(Dictionary<object, dynamic> dictionary)
4501 var sb =
new StringBuilder();
4503 if (0 == dictionary.Count)
4505 GPAL.
PublishSimpleEvent(GPALEventType.WARNING,
"Dictionary is empty. No html will be output.", dictionary, GPALObjectType.Converter);
4506 return string.Empty;
4509 if (dictionary.ContainsKey(
"tag"))
4511 var tagName = dictionary[
"tag"].ToString();
4512 if (
"#comment".Equals(tagName))
4514 if (dictionary.ContainsKey(
"value"))
4516 sb.Append($
" {dictionary["value
"].ToString()}\n");
4518 return sb.ToString();
4521 if (
false ==
"#text".Equals(tagName))
4523 sb.Append($
"<{tagName}");
4524 if (dictionary.ContainsKey(
"attributes"))
4526 dynamic attributes = dictionary[
"attributes"];
4527 foreach (var attribute
in attributes)
4529 sb.Append($
" {attribute.Key}=\"{attribute.Value}\"");
4554 if (dictionary.ContainsKey(
"text"))
4556 sb.Append(dictionary[
"text"].ToString());
4559 if (dictionary.ContainsKey(
"children"))
4561 var children = (List<Dictionary<object, dynamic>>)dictionary[
"children"];
4562 foreach (var child
in children)
4564 if (
false == child.ContainsKey(
"#text"))
4566 string text = ConvertDictionaryToHtml(child);
4567 if (0 < text.Length)
4571 sb.Append(child[
"text"]);
4586 sb.Append($
"</{tagName}>\n");
4593 bool isArray = IsArrayLike(dictionary);
4596 sb.Append($
"<ul id=list style='margin: 8px; padding-left: 8px;'>\n");
4598 foreach (var kvp
in dictionary)
4600 dynamic item = kvp.Value;
4601 string toRender = RenderValue(item,
true,
"style='margin: 8px; padding-left: 8px;");
4603 if (
false ==
string.IsNullOrEmpty(toRender))
4606 sb.Append(toRender);
4607 sb.Append(
"</li>\n");
4615 sb.Append(RenderFullObjectToHtml(dictionary));
4618 return sb.ToString();
4629 var dictionaries =
new List<Dictionary<object, dynamic>>();
4630 HtmlNodeCollection nodes =
null;
4632 if (inputDataOrFilename is HtmlNodeCollection)
4633 nodes = inputDataOrFilename;
4635 nodes = GetHtmlNodes(inputDataOrFilename);
4639 GPAL.
PublishSimpleEvent(GPALEventType.WARNING,
"No html nodes found. Dictionary will be empty", inputDataOrFilename, GPALObjectType.Converter);
4640 return new List<Dictionary<object, dynamic>>();
4643 foreach (var node
in nodes)
4646 if (node.Name ==
"#document" &&
false == isChildren)
4650 if (
true ==
"#comment".Equals(node.Name))
4652 if (
false == isChildren)
4655 var commentDict =
new Dictionary<object, dynamic>
4657 {
"tag",
"#comment" },
4658 {
"value", node.InnerHtml }
4660 dictionaries.Add(commentDict);
4665 if (
true ==
"#text".Equals(node.Name))
4667 if (
false == isChildren)
4670 var textDict =
new Dictionary<object, dynamic>
4673 {
"value", node.InnerHtml }
4675 dictionaries.Add(textDict);
4679 var dict =
new Dictionary<object, dynamic>();
4682 dict[
"tag"] = node.Name;
4683 if (node.Attributes.Count > 0)
4685 var attributes =
new Dictionary<string, string>();
4686 foreach (var attribute
in node.Attributes)
4688 attributes[attribute.Name] = attribute.Value;
4690 dict[
"attributes"] = attributes;
4694 if (!
string.IsNullOrEmpty(node.InnerText.Trim()) && !node.HasChildNodes)
4696 dict[
"text"] = node.InnerText.Trim();
4700 if (node.HasChildNodes)
4702 var children =
new List<Dictionary<object, dynamic>>();
4703 foreach (var childNode
in node.ChildNodes)
4705 if (childNode.Name ==
"#text")
4707 if (!
string.IsNullOrEmpty(childNode.InnerHtml.Trim()))
4710 if (node.ChildNodes.All(n => n.Name ==
"#text"))
4712 dict[
"text"] = childNode.InnerHtml.Trim();
4717 children.Add(
new Dictionary<object, dynamic>
4720 {
"value", childNode.InnerHtml }
4728 if (childDictionaries.Count > 0)
4730 foreach (var childDict
in childDictionaries)
4732 children.Add(childDict);
4737 if (children.Count > 0)
4738 dict[
"children"] = children;
4741 dictionaries.Add(dict);
4743 return dictionaries;
4745 public static HtmlNodeCollection GetHtmlNodes(dynamic htmlorFilename)
4748 var doc =
new HtmlAgilityPack.HtmlDocument();
4751 htmlString = File.ReadAllText(htmlorFilename);
4755 htmlString = htmlorFilename;
4760 doc.LoadHtml(htmlString);
4761 return doc.DocumentNode.ChildNodes;
4763 catch (Exception ex)
4765 GPAL.
PublishSimpleEvent(GPALEventType.ERROR, $
"Expected html string or GPALFile, got [{htmlorFilename.GetType()}]", htmlorFilename, GPALObjectType.Other, ex);
4770 public static dynamic CreateIEnumerableFromClass<T>(
ConverterSettings converterSettings)
4772 IEnumerable<T> resultList =
null;
4773 bool isArrayOutput = converterSettings.OutputClassType.IsArray;
4774 T[] arrayOutput =
null;
4775 int arrayCapacity = 0;
4776 int elementIndex = 0;
4781 arrayCapacity = ((Array)Activator.CreateInstance(converterSettings.OutputClassType, 4)).Length;
4785 arrayCapacity = converterSettings.InputDictionary.Count();
4791 arrayOutput = (T[])Array.CreateInstance(converterSettings.OutputClassType.GetElementType(), arrayCapacity);
4793 for (
int i = 0; i < arrayCapacity; i++)
4795 arrayOutput[i] = (T)InstantiateOne(converterSettings.OutputClassElementType);
4800 switch (converterSettings.OutputClassType)
4803 resultList = (IEnumerable<T>)ActivatorHelper.CreateInstance(converterSettings.OutputClassType);
4810 foreach (var dict
in converterSettings.InputDictionary)
4812 if (isArrayOutput && inputIndex >= arrayCapacity)
4817 bool allKeysSame =
false;
4820 allKeysSame = CheckKeysPrefix(dict);
4822 catch (Exception ex)
4824 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"CheckKeysPrefix exception", converterSettings, GPALObjectType.Other, ex);
4828 int dictCount = ObjectCopier.GetItemCount(
null, dict);
4830 foreach (var dict2
in dict)
4832 T element = (T)(
object)InstantiateOne(converterSettings.OutputClassElementType);
4834 dynamic retVal =
null;
4835 if (
true == allKeysSame)
4836 retVal = ConvertToClass(dict, converterSettings.OutputClassType, dict,
null);
4838 retVal = ConvertToClass(dict2.Value, converterSettings.OutputClassElementType, dict,
null);
4842 int count = ObjectCopier.GetItemCount(
null, retVal);
4847 foreach (dynamic element2
in retVal)
4849 arrayOutput[elementIndex++] = (T)element2;
4852 arrayOutput[elementIndex++] = (T)retVal;
4854 catch (IndexOutOfRangeException)
4856 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"[{converterSettings.OutputClassType.Name}] does not have enough capacity for [{dictCount}] items. Returning [{elementIndex - 1}]", retVal, GPALObjectType.Other);
4860 else if (converterSettings.OutputClassType == retVal.GetType())
4864 element = (T)retVal;
4871 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
4873 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { typeof(List<>).MakeGenericType(ConverterSettings.OutputClassElementType) },
null);
4874 MethodInfo enqueueMethod =
ConverterSettings.OutputClassType.GetMethod(
"Enqueue",
4875 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
4877 BindingFlags.Public | BindingFlags.Instance,
null,
new[] { ConverterSettings.OutputClassElementType },
null);
4879 if (
null != addRowMethod)
4881 converterSettings.OutputData = converterSettings.InputData[dictIdx];
4882 if (
null != converterSettings.InDelimiter &&
null != converterSettings.OutDelimiter)
4883 converterSettings.OutputData = converterSettings.InputData[dictIdx].Replace(converterSettings.InDelimiter.Value, converterSettings.OutDelimiter.Value);
4884 string[] lines = converterSettings.OutputData.Split(
new[] {
'\r',
'\n' }, StringSplitOptions.RemoveEmptyEntries);
4886 foreach (
object line
in lines)
4888 addRowMethod.Invoke(resultList,
new object[] {
new List<object>() { (dynamic)line } });
4891 else if (
null != addMethod)
4892 addMethod.Invoke(resultList,
new object[] { element });
4893 else if (
null != enqueueMethod)
4894 enqueueMethod.Invoke(resultList,
new object[] { element });
4895 else if (
null != pushMethod)
4896 pushMethod.Invoke(resultList,
new object[] { element });
4898 GPAL.
PublishSimpleEvent(GPALEventType.ERROR, $
"Can't find 'Add/Enqueue/Push' method for [{converterSettings.OutputClassType}]", converterSettings, GPALObjectType.Other);
4901 catch (Exception ex)
4903 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Can't 'Add' to [{converterSettings.OutputClassType}]", converterSettings, GPALObjectType.Other, ex);
4906 if (
false == allKeysSame)
4914 return isArrayOutput ? arrayOutput : resultList;
4916 public static List<T> CreateListFromClass<T>(
ConverterSettings converterSettings)
4918 List<T> resultList =
new List<T>();
4920 foreach (var data
in converterSettings.InputDictionary)
4922 dynamic element = InstantiateOne(converterSettings.OutputClassElementType);
4923 element = ConvertToClass(
new List<Dictionary<object, dynamic>>() { data }, converterSettings.OutputClassElementType, converterSettings.InputDictionary,
null);
4925 resultList.Add(element);
4929 public static dynamic InstantiateOne(Type objectType,
int arrayLength = 0)
4931 if (objectType ==
null)
4933 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"ObjectType is null in InstantiateOne",
null, GPALObjectType.Other);
4937 if (objectType.IsArray)
4939 Type elementType = objectType.GetElementType();
4940 Array dynamicArray = Array.CreateInstance(elementType, arrayLength);
4941 for (
int i = 0; i < arrayLength; i++)
4943 dynamicArray.SetValue(InstantiateOne(elementType), i);
4945 return dynamicArray;
4947 else if (objectType == typeof(
string))
4949 return string.Empty;
4951 else if (objectType.IsValueType)
4953 return ActivatorHelper.CreateInstance(objectType);
4955 else if (IsAnonymousType(objectType))
4957 return new System.Dynamic.ExpandoObject();
4964 return ActivatorHelper.CreateInstance(objectType);
4966 catch (Exception exInner)
4972 Type[] genericArgs = objectType.IsGenericType
4973 ? objectType.GetGenericArguments()
4974 :
new[] { typeof(
object), typeof(
object) };
4976 Type keyType = genericArgs[0];
4977 Type valueType = genericArgs.Length > 1 ? genericArgs[1] : typeof(
object);
4979 Type concreteDictType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType);
4980 return ActivatorHelper.CreateInstance(concreteDictType);
4982 catch (Exception ex)
4985 $
"Failed to instantiate dictionary type [{objectType}] (fallback also failed: [{exInner.Message}])",
4986 null, GPALObjectType.Other, ex);
4987 return new System.Dynamic.ExpandoObject();
4996 return ActivatorHelper.CreateInstance(objectType);
4998 catch (MissingMethodException)
5001 var constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
5002 foreach (var ctor
in constructors)
5004 var parameters = ctor.GetParameters();
5005 var args =
new object[parameters.Length];
5006 for (
int i = 0; i < parameters.Length; i++)
5008 var paramType = parameters[i].ParameterType;
5009 if (paramType == typeof(
string))
5010 args[i] =
string.Empty;
5011 else if (paramType == typeof(
int))
5013 else if (paramType == typeof(Dictionary<string, string>))
5014 args[i] =
new Dictionary<string, string>();
5015 else if (paramType.IsValueType)
5016 args[i] = ActivatorHelper.CreateInstance(paramType);
5022 return ctor.Invoke(args);
5029 GPAL.
PublishSimpleEvent(GPALEventType.ERROR, $
"No suitable constructor for type [{objectType}]",
null, GPALObjectType.Other);
5036 private static bool IsAnonymousType(Type type)
5038 return Attribute.IsDefined(type, typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute),
false) &&
5039 type.IsGenericType &&
5040 type.Name.Contains(
"Anonymous") &&
5041 (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
5044 private static Complex ComplexFromDict(dynamic value)
5047 dynamic dict = value;
5048 if (value is IEnumerable && !(value is IDictionary) && !(value is
string))
5049 foreach (var item
in (IEnumerable)value) { dict = item;
break; }
5051 double real = 0, imaginary = 0;
5052 if (dict is IDictionary idict)
5054 foreach (DictionaryEntry entry
in idict)
5056 string rawEntryKey = entry.Key?.ToString() ??
string.Empty;
5057 string key = rawEntryKey.StartsWith(
"GPALKEY", StringComparison.Ordinal)
5058 ? rawEntryKey.Substring(7)
5059 : (System.Text.RegularExpressions.Regex.IsMatch(rawEntryKey,
@"^GPAL\d+_")
5060 ? System.Text.RegularExpressions.Regex.Replace(rawEntryKey,
@"^GPAL\d+_",
"")
5064 object val = entry.Value;
5065 if (val is IEnumerable valEnum && !(val is
string))
5066 foreach (var v
in valEnum) { val = v;
break; }
5068 string str = val?.ToString();
5069 if (key.Equals(
"Real", StringComparison.OrdinalIgnoreCase))
5070 double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out real);
5071 else if (key.Equals(
"Imaginary", StringComparison.OrdinalIgnoreCase))
5072 double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out imaginary);
5075 return new Complex(real, imaginary);
5078 internal static dynamic ParseComplex(
string complexString)
5080 string s = complexString.Trim();
5082 string numPart = s.Substring(0, s.Length - 1);
5084 int signIndex = Math.Max(numPart.LastIndexOf(
'+'), numPart.LastIndexOf(
'-'));
5085 if (signIndex == -1)
throw new FormatException(
"No sign found");
5087 string realStr = numPart.Substring(0, signIndex);
5088 string imagStr = numPart.Substring(signIndex);
5090 double real =
string.IsNullOrEmpty(realStr) ? 0 :
double.Parse(realStr, CultureInfo.InvariantCulture);
5091 double imag =
double.Parse(imagStr, CultureInfo.InvariantCulture);
5093 return new Complex(real, imag);
5096 public static bool AddToList(dynamic listQueueStackDictHashsetOrArray, dynamic value,
int count)
5099 value = EnsureCollectionType(listQueueStackDictHashsetOrArray, value);
5101 if ((
true == IsEnumerableObject(listQueueStackDictHashsetOrArray) &&
false == IsArray(listQueueStackDictHashsetOrArray)) ||
true == IsList(listQueueStackDictHashsetOrArray))
5106 MethodInfo addMethod = listQueueStackDictHashsetOrArray.GetType().GetMethod(
"Add",
5107 BindingFlags.Public | BindingFlags.Instance);
5108 MethodInfo enqueueMethod = listQueueStackDictHashsetOrArray.GetType().GetMethod(
"Enqueue",
5109 BindingFlags.Public | BindingFlags.Instance);
5110 MethodInfo pushMethod = listQueueStackDictHashsetOrArray.GetType().GetMethod(
"Push",
5111 BindingFlags.Public | BindingFlags.Instance);
5113 if (
null != addMethod)
5115 if (
true ==
ConverterHelper.IsDictionaryType(listQueueStackDictHashsetOrArray.GetType()))
5116 addMethod.Invoke(listQueueStackDictHashsetOrArray,
new object[] { count, value });
5118 addMethod.Invoke(listQueueStackDictHashsetOrArray,
new object[] { value });
5120 else if (
null != enqueueMethod)
5121 enqueueMethod.Invoke(listQueueStackDictHashsetOrArray,
new object[] { value });
5122 else if (
null != pushMethod)
5123 pushMethod.Invoke(listQueueStackDictHashsetOrArray,
new object[] { value });
5125 catch (Exception ex)
5127 GPAL.
PublishSimpleEvent(GPALEventType.DEBUG, $
"Can't Add [{value}] to [{listQueueStackDictHashsetOrArray}]. May require a Convert.ChangeType", listQueueStackDictHashsetOrArray, GPALObjectType.Other, ex);
5132 else if (
true == IsArray(listQueueStackDictHashsetOrArray))
5133 listQueueStackDictHashsetOrArray[count] = EnsureCollectionType(listQueueStackDictHashsetOrArray, value);
5146 private static object EnsureCollectionType(
object container,
object value)
5148 if (container ==
null || value ==
null)
return value;
5149 Type containerType = container.GetType();
5152 Type targetElementType = containerType.IsArray ? containerType.GetElementType() : (containerType.IsGenericType ? containerType.GetGenericArguments()[0] :
null);
5156 if (targetElementType ==
null || targetElementType.IsAssignableFrom(value.GetType()))
5160 object convertedItem = ConvertValue(value, targetElementType, container);
5163 return WrapInCollection(targetElementType, convertedItem);
5186 private static object WrapInCollection(Type typeToCreate, dynamic item)
5189 if (typeToCreate.IsArray)
5191 Array newArray = Array.CreateInstance(typeToCreate.GetElementType(), 1);
5192 newArray.SetValue(item, 0);
5196 if (typeToCreate == item.GetType())
5200 object collection = Activator.CreateInstance(typeToCreate);
5203 MethodInfo addMethod = typeToCreate.GetMethod(
"Add")
5204 ?? typeToCreate.GetMethod(
"Push")
5205 ?? typeToCreate.GetMethod(
"Enqueue");
5207 addMethod?.Invoke(collection,
new[] { item });
5211 internal static string UppercaseFirst(
string input)
5213 if (
string.IsNullOrEmpty(input))
5216 return char.ToUpper(input[0]) + input.Substring(1);
5219 static bool CheckKeysPrefix(Dictionary<dynamic, dynamic> dict)
5221 if (!dict.Any())
return true;
5223 string commonPrefix =
null;
5225 foreach (var entry
in dict.Values)
5231 IEnumerable<string> innerKeys;
5237 var innerDict = (IDictionary<string, object>)entry;
5238 innerKeys = innerDict.Keys;
5246 if (!innerKeys.Any())
5249 string currentKey = innerKeys.First();
5251 string prefix = GetPrefixUpToNumber(currentKey);
5253 if (
string.IsNullOrEmpty(prefix))
5256 if (commonPrefix ==
null)
5258 commonPrefix = prefix;
5260 else if (currentKey != prefix && !currentKey.StartsWith(commonPrefix))
5269 static string GetPrefixUpToNumber(
string key)
5271 if (
string.IsNullOrEmpty(key))
return string.Empty;
5274 while (i < key.Length && !
char.IsDigit(key[i]))
5277 return i > 0 ? key.Substring(0, i) :
string.Empty;
5279 public static Type GetConcreteType(Type type)
5281 if (type ==
null)
throw new ArgumentNullException(nameof(type));
5284 if (!type.IsInterface && !type.IsAbstract)
5290 return type.Assembly.GetTypes()
5291 .FirstOrDefault(t => !t.IsInterface && !t.IsAbstract && type.IsAssignableFrom(t))
5292 ??
throw new InvalidOperationException($
"No concrete implementation found for {type.Name}.");
5294 private static object ConvertValue(dynamic value, Type targetType, dynamic parentDictionary =
null,
string propertyName =
null)
5296 string fromCtx = propertyName !=
null ? $
" from [{propertyName}]" :
string.Empty;
5299 bool isNullInput =
false;
5301 if (targetType == typeof(
object))
5305 if (IsTupleType(value?.GetType()) && IsTupleType(targetType))
5307 Type targetTupleType = Nullable.GetUnderlyingType(targetType) ?? targetType;
5310 if (value is System.Runtime.CompilerServices.ITuple sourceTuple)
5312 int arity = sourceTuple.Length;
5313 Type[] targetElementTypes = targetTupleType.GetGenericArguments();
5316 if (arity == targetElementTypes.Length)
5318 var convertedItems =
new object[arity];
5319 for (
int i = 0; i < arity; i++)
5321 object item = sourceTuple[i];
5322 convertedItems[i] = ConvertValue(item, targetElementTypes[i]);
5327 var ctor = targetTupleType.GetConstructor(targetElementTypes);
5330 return ctor.Invoke(convertedItems);
5338 bool isStruct = IsCustomStructType(value?.GetType());
5340 Type ut2 = Nullable.GetUnderlyingType(targetType) ?? targetType;
5341 bool isCharTarget = ut2 == typeof(
char);
5342 isNullInput =
false == isStruct
5344 (value is
string str && !isCharTarget &&
string.IsNullOrWhiteSpace(str)) ||
5345 (value is
string s2 && s2.Trim().Equals(
"null", StringComparison.OrdinalIgnoreCase))
5355 if (IsNullableType(targetType) || targetType.IsClass)
5359 $
"Cannot convert [{DescribeForLog(value)}] to non-nullable [{ShortTypeName(targetType)}]{fromCtx}. Returning default.",
5360 null, GPALObjectType.Other);
5361 return Activator.CreateInstance(targetType);
5364 Type underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType;
5367 if (IsSupportedCollectionType(underlyingTargetType))
5369 Type elementType = underlyingTargetType.IsArray
5370 ? underlyingTargetType.GetElementType()!
5371 : underlyingTargetType.GetGenericArguments()[0];
5373 IEnumerable<object> sourceItems = (value is IEnumerable enumerable && !(value is string))
5374 ? enumerable.Cast<
object>()
5377 var convertedItems =
new List<object>();
5379 foreach (var item
in sourceItems)
5381 object converted = ConvertValue(item, elementType);
5382 convertedItems.Add(converted);
5386 return CreateCollection(convertedItems, underlyingTargetType);
5392 if (value is
string floatStr)
5394 string trimmed = floatStr.Trim().ToLowerInvariant();
5395 if (underlyingTargetType == typeof(
float) || underlyingTargetType == typeof(
double))
5397 if (trimmed ==
"nan" || trimmed ==
".nan")
5398 return underlyingTargetType == typeof(
float) ? (object)
float.NaN : double.NaN;
5399 if (trimmed ==
"infinity" || trimmed ==
".inf" || trimmed ==
"+infinity" || trimmed ==
"+.inf")
5400 return underlyingTargetType == typeof(
float) ? (object)
float.PositiveInfinity : double.PositiveInfinity;
5401 if (trimmed ==
"-infinity" || trimmed ==
"-.inf")
5402 return underlyingTargetType == typeof(
float) ? (object)
float.NegativeInfinity : double.NegativeInfinity;
5407 if (IsEnumType(underlyingTargetType))
5409 string strValue = value.ToString().Trim();
5412 var tryParseMethod = typeof(Enum)
5413 .GetMethods(BindingFlags.Public | BindingFlags.Static)
5414 .FirstOrDefault(m =>
5415 m.Name == nameof(Enum.TryParse)
5416 && m.IsGenericMethodDefinition
5417 && m.GetGenericArguments().Length == 1
5418 && m.GetParameters().Length == 3
5419 && m.GetParameters()[0].ParameterType == typeof(
string)
5420 && m.GetParameters()[1].ParameterType == typeof(
bool)
5421 && m.GetParameters()[2].IsOut);
5423 if (tryParseMethod !=
null)
5425 var genericMethod = tryParseMethod.MakeGenericMethod(underlyingTargetType);
5426 object[] parameters =
new object[] { strValue,
true,
null };
5427 bool success = (bool)genericMethod.Invoke(
null, parameters);
5430 return parameters[2];
5435 if (
long.TryParse(strValue, out
long numericValue))
5439 return Enum.ToObject(underlyingTargetType, numericValue);
5449 $
"Invalid enum value [{value}] for type [{ShortTypeName(underlyingTargetType)}]{fromCtx}. Returning default (0).",
5450 null, GPALObjectType.Other);
5452 return Enum.ToObject(underlyingTargetType, 0);
5456 if (value is
string strValue2)
5458 string trimmed = strValue2.Trim();
5460 if (IsGuidType(underlyingTargetType))
5462 if (Guid.TryParse(trimmed, out Guid guid))
5465 if (trimmed.Length > 1 && trimmed[0] ==
'_' && Guid.TryParse(trimmed.Substring(1), out Guid strippedGuid))
5466 return strippedGuid;
5467 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Guid format: [{strValue2}]{fromCtx}. Returning Guid.Empty.",
null, GPALObjectType.Other);
5471 if (IsIpAddressType(underlyingTargetType))
5473 if (IPAddress.TryParse(trimmed, out IPAddress ip))
5475 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid IPAddress format: [{strValue2}]{fromCtx}. Returning IPAddress.Any.",
null, GPALObjectType.Other);
5476 return IPAddress.Any;
5479 if (IsUriType(underlyingTargetType))
5481 if (Uri.TryCreate(trimmed, UriKind.RelativeOrAbsolute, out Uri uri))
5483 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Uri format: [{strValue2}]{fromCtx}. Returning null.",
null, GPALObjectType.Other);
5487 if (IsTimeSpanType(underlyingTargetType))
5489 if (TimeSpan.TryParse(trimmed, CultureInfo.InvariantCulture, out TimeSpan ts))
5491 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid TimeSpan format: [{strValue2}]{fromCtx}. Returning TimeSpan.Zero.",
null, GPALObjectType.Other);
5492 return TimeSpan.Zero;
5497 if (IsDateTimeOffsetType(underlyingTargetType))
5499 if (DateTimeOffset.TryParse(trimmed, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTimeOffset dto))
5501 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid DateTimeOffset format: [{strValue2}]{fromCtx}. Returning DateTimeOffset.MinValue.",
null, GPALObjectType.Other);
5502 return DateTimeOffset.MinValue;
5505 if (IsBigIntegerType(underlyingTargetType))
5507 if (BigInteger.TryParse(trimmed, NumberStyles.Integer, CultureInfo.InvariantCulture, out BigInteger bi))
5509 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid BigInteger format: [{strValue2}]{fromCtx}. Returning 0.",
null, GPALObjectType.Other);
5510 return BigInteger.Zero;
5513 if (IsComplexType(underlyingTargetType))
5517 return ParseComplex(trimmed);
5521 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Complex format: [{strValue2}]{fromCtx}. Returning 0+0i.",
null, GPALObjectType.Other);
5522 return new Complex(0, 0);
5526 if (IsVersionType(underlyingTargetType))
5528 if (System.Version.TryParse(trimmed, out System.Version ver))
5530 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Version format: [{strValue2}]{fromCtx}. Returning 0.0.",
null, GPALObjectType.Other);
5531 return new System.Version(0, 0);
5536 if (
true == IsVersionType(underlyingTargetType))
5538 if (value.GetType() == typeof(System.Version))
5541 List<int> versionInfo =
new List<int>();
5544 foreach (KeyValuePair<object, object> item
in value)
5548 versionInfo.Add(Int32.Parse(item.Value.ToString()));
5551 return new System.Version(versionInfo[0], versionInfo[1], versionInfo[2], versionInfo[3]);
5554 if (typeof(WaitTime) == underlyingTargetType)
5557 if (
true == TryConvertToWaitTime(value, out wt))
5561 if (
true == IsCustomStructType(underlyingTargetType))
5565 if (value.GetType() == underlyingTargetType)
5568 return ConvertToClass(value, underlyingTargetType, parentDictionary,
null);
5570 catch (Exception ex)
5573 $
"Failed to convert [{value}] [{ShortTypeName(value?.GetType())}] to [{ShortTypeName(underlyingTargetType)}]{fromCtx}. Returning default.",
5574 null, GPALObjectType.Other, ex);
5575 return Activator.CreateInstance(underlyingTargetType);
5580 if (IsBigIntegerType(underlyingTargetType))
5582 try {
return AsBigInteger(value); }
5585 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid BigInteger value: [{value}]{fromCtx}. Returning 0.",
null, GPALObjectType.Other);
5586 return BigInteger.Zero;
5591 if (IsSimpleType(underlyingTargetType))
5595 if (value is BigInteger bigIntSrc)
5597 try {
return System.Convert.ChangeType(bigIntSrc.ToString(CultureInfo.InvariantCulture), underlyingTargetType, CultureInfo.InvariantCulture); }
5598 catch (Exception ex)
5600 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Failed to convert BigInteger [{value}] to [{ShortTypeName(underlyingTargetType)}]{fromCtx}. Returning default.",
null, GPALObjectType.Other, ex);
5601 return Activator.CreateInstance(underlyingTargetType);
5606 return System.Convert.ChangeType(value, underlyingTargetType, CultureInfo.InvariantCulture);
5608 catch (Exception ex)
5611 $
"Failed to convert [{value}] [{ShortTypeName(value?.GetType())}] to [{ShortTypeName(underlyingTargetType)}]{fromCtx}. Returning default.",
5612 null, GPALObjectType.Other, ex);
5613 return Activator.CreateInstance(underlyingTargetType);
5618 if (targetType.IsAssignableFrom(value.GetType()))
5623 if (value is
string cvCycleStr && cvCycleStr.StartsWith(
"Recursion to [") && cvCycleStr.EndsWith(
"]") && targetType != typeof(
string))
5624 return targetType.IsValueType ? Activator.CreateInstance(targetType) :
null;
5627 if (value is IDictionary dictSrc && IsClassType(targetType) && !IsDictionaryType(targetType))
5629 try {
return CreateObjectFromDictionary(dictSrc, targetType, dictSrc,
null); }
5630 catch (Exception ex)
5632 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Unable to reconstruct [{ShortTypeName(targetType)}] from dict{fromCtx}", value, GPALObjectType.None, ex);
5637 if (value is IList cvListVal && IsClassType(targetType) && !IsDictionaryType(targetType) && !IsSupportedCollectionType(targetType))
5639 object cvInner = cvListVal;
5640 while (cvInner is IList cvInnerList && cvInnerList.Count == 1)
5641 cvInner = cvInnerList[0];
5642 if (cvInner is IDictionary cvInnerDict)
5644 try {
return CreateObjectFromDictionary(cvInnerDict, targetType, cvInnerDict,
null); }
5645 catch (Exception ex)
5647 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Unable to reconstruct [{ShortTypeName(targetType)}] from unwrapped dict{fromCtx}", value, GPALObjectType.None, ex);
5654 $
"Unable to convert [{DescribeForLog(value)}] to [{ShortTypeName(targetType)}]{fromCtx}",
5655 null, GPALObjectType.Other);
5659 private static string ShortTypeName(Type t)
5661 if (t ==
null)
return "null";
5662 if (IsNullableType(t))
5663 return ShortTypeName(Nullable.GetUnderlyingType(t)) +
"?";
5664 if (!t.IsGenericType)
return t.Name;
5666 return "(" +
string.Join(
", ", t.GetGenericArguments().Select(a => ShortTypeName(a))) +
")";
5667 string baseName = t.Name.Substring(0, t.Name.IndexOf(
'`'));
5668 string args =
string.Join(
", ", t.GetGenericArguments().Select(a => ShortTypeName(a)));
5669 return $
"{baseName}<{args}>";
5672 private static string CompactValue(
object v,
int depth = 0)
5674 if (v ==
null)
return "null";
5675 if (v is
string s)
return s.Length <= 40 ? s : s.Substring(0, 40) +
"...";
5678 if (v is IDictionary)
return "{...}";
5679 if (v is IEnumerable)
return "[...]";
5681 if (v is IDictionary dict)
5683 var pairs =
new List<string>();
5684 foreach (dynamic kvp
in dict) { pairs.Add($
"{kvp.Key}={CompactValue(kvp.Value, depth + 1)}");
if (pairs.Count >= 3)
break; }
5685 return "{" +
string.Join(
", ", pairs) + (dict.Count > 3 ?
", ..." :
"") +
"}";
5687 if (v is IEnumerable seq)
5689 var items =
new List<string>();
5690 foreach (var item
in seq) { items.Add(CompactValue(item, depth + 1));
if (items.Count >= 2)
break; }
5691 return "[" +
string.Join(
", ", items) + (items.Count >= 2 ?
", ..." :
"") +
"]";
5695 string str = v.ToString();
5696 return str == v.GetType().FullName ? ShortTypeName(v.GetType()) : str;
5698 catch {
return ShortTypeName(v.GetType()); }
5701 private static string DescribeForLog(
object value,
int maxLen = 80)
5703 if (value ==
null)
return "null";
5704 if (value is
string s)
5705 return s.Length <= maxLen ? s : s.Substring(0, maxLen) +
"...";
5706 if (value is IDictionary dict)
5708 var pairs =
new List<string>();
5709 foreach (dynamic kvp
in dict) { pairs.Add($
"{kvp.Key}={CompactValue(kvp.Value)}");
if (pairs.Count >= 3)
break; }
5710 return "{" +
string.Join(
", ", pairs) + (dict.Count > 3 ?
", ..." :
"") +
"}";
5712 if (value is IEnumerable seq)
5714 var items =
new List<string>();
5715 foreach (var item
in seq) { items.Add(CompactValue(item));
if (items.Count >= 3)
break; }
5716 return "[" +
string.Join(
", ", items) + (items.Count >= 3 ?
", ..." :
"") +
"]";
5720 string str = value.ToString();
5721 return str == value.GetType().FullName ? ShortTypeName(value.GetType()) : (str.Length <= maxLen ? str : str.Substring(0, maxLen) +
"...");
5723 catch {
return ShortTypeName(value.GetType()); }
5726 private static object CreateCollection(List<object> convertedItems, Type targetCollectionType)
5728 Type underlying = targetCollectionType;
5729 if (targetCollectionType.IsGenericType &&
5730 targetCollectionType.GetGenericTypeDefinition() == typeof(Nullable<>))
5732 underlying = Nullable.GetUnderlyingType(targetCollectionType)!;
5735 if (underlying.IsArray)
5737 Type elementType = underlying.GetElementType()!;
5738 Array array = Array.CreateInstance(elementType, convertedItems.Count);
5739 for (
int i = 0; i < convertedItems.Count; i++)
5741 object converted = ConvertValue(convertedItems[i], elementType);
5742 if (converted ==
null || elementType.IsAssignableFrom(converted.GetType()))
5743 array.SetValue(converted, i);
5745 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Unable to convert [{DescribeForLog(converted)}] to [{elementType.Name}]", converted, GPALObjectType.None);
5751 if (underlying.IsGenericType)
5753 Type[] genericArgs = underlying.GetGenericArguments();
5754 Type elementType = genericArgs[0];
5756 Type listType = typeof(List<>).MakeGenericType(elementType);
5757 IList list = (IList)Activator.CreateInstance(listType)!;
5759 foreach (var rawItem
in convertedItems)
5761 object convertedItem = ConvertValue(rawItem, elementType);
5762 if (convertedItem ==
null || elementType.IsAssignableFrom(convertedItem.GetType()))
5763 list.Add(convertedItem);
5765 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"Unable to convert [{DescribeForLog(convertedItem)}] to [{elementType.Name}]", convertedItem, GPALObjectType.None);
5769 if (underlying.IsGenericType &&
5770 underlying.GetGenericTypeDefinition() == typeof(List<>))
5779 var targetListType = targetCollectionType;
5780 var ctor = targetListType.GetConstructor(
new[] { typeof(IEnumerable<>).MakeGenericType(elementType) });
5782 return ctor.Invoke(
new object[] { list });
5785 ctor = targetListType.GetConstructor(
new[] { listType });
5787 return ctor.Invoke(
new object[] { list });
5796 IList nonGenericList =
new ArrayList();
5797 foreach (var item
in convertedItems)
5798 nonGenericList.Add(item);
5799 return nonGenericList;
5802 private static object ConvertDictionaryToStruct(
5803 IDictionary<string, object> dict,
5805 HashSet<object> visited)
5808 object instance = Activator.CreateInstance(structType);
5810 foreach (var prop
in structType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
5815 if (dict.TryGetValue(prop.Name, out var rawValue))
5817 object converted = ConvertValue(
5822 prop.SetValue(instance, converted);
5830 public static object NormalizeForCleanJson(
object input)
5833 if (input is Dictionary<object, dynamic> dict)
5836 bool hasXmlMarkers = dict.Keys.Cast<
object>().Any(k => k.ToString().StartsWith(
"@GPAL") || k.ToString() ==
"#text");
5847 bool hasComplexKey = dict.Keys.Cast<
object>().Any(k => k !=
null &&
false == IsSimpleType(k.GetType()));
5850 var pairList =
new List<object>();
5851 foreach (var kvp
in dict)
5853 pairList.Add(
new Dictionary<string, object>
5855 [
"Key"] = NormalizeForCleanJson(ConvertClassToDictionary(kvp.Key)),
5856 [
"Value"] = NormalizeForCleanJson(kvp.Value)
5863 var result =
new Dictionary<string, object>();
5864 foreach (var kvp
in dict)
5866 string key = kvp.Key.ToString().Replace(
"@GPAL_",
"");
5867 result[key] = NormalizeForCleanJson(kvp.Value);
5873 var clean =
new Dictionary<string, object>();
5875 string textValue =
null;
5876 if (dict.TryGetValue(
"#text", out var txt))
5878 textValue = txt?.ToString();
5882 foreach (var kvp
in dict)
5884 string key = kvp.Key.ToString();
5886 if (key.StartsWith(
"@GPAL_"))
5888 clean[key.Substring(6)] = kvp.Value;
5893 foreach (var kvp
in dict)
5895 string key = kvp.Key.ToString();
5896 if (key.StartsWith(
"@GPAL_") || key ==
"#text")
continue;
5898 object child = NormalizeForCleanJson(kvp.Value);
5905 if (textValue !=
null && clean.Count > 0)
5907 clean[
"value"] = textValue;
5912 if (textValue !=
null && clean.Count == 0)
5914 if (
double.TryParse(textValue, out
double num))
5921 else if (input is List<Dictionary<object, dynamic>> list)
5923 return list.Select(NormalizeForCleanJson).ToList();
5925 else if (input is List<object> objList)
5927 return objList.Select(NormalizeForCleanJson).ToList();
5940 object phase1 = StripAndGroup(input);
5943 return UnwrapSingleItemLists(phase1);
5946 private static object StripAndGroup(
object input)
5948 if (input ==
null)
return null;
5950 if (input is IDictionary dict)
5952 var grouped =
new Dictionary<string, List<object>>(StringComparer.Ordinal);
5954 foreach (DictionaryEntry entry
in dict)
5956 string originalKey = entry.Key?.ToString() ??
string.Empty;
5957 string cleanedKey = originalKey;
5959 if (cleanedKey.StartsWith(
"@GPAL_"))
5960 cleanedKey = cleanedKey.Substring(6);
5961 else if (cleanedKey.StartsWith(
"@"))
5962 cleanedKey = cleanedKey.Substring(1);
5964 object value = StripAndGroup(entry.Value);
5971 if (value is List<object> singleWrap && singleWrap.Count == 1 && singleWrap[0] is IDictionary)
5972 value = singleWrap[0];
5975 if (
string.IsNullOrEmpty(cleanedKey) && value is
string str && !
string.IsNullOrWhiteSpace(str))
5976 cleanedKey =
"value";
5978 else if (cleanedKey ==
"#text")
5979 cleanedKey =
"value";
5980 else if (cleanedKey.StartsWith(
"GPALKEY"))
5981 cleanedKey = cleanedKey.Substring(7);
5987 else if (System.Text.RegularExpressions.Regex.IsMatch(cleanedKey,
@"^GPAL\d+_"))
5988 cleanedKey = System.Text.RegularExpressions.Regex.Replace(cleanedKey,
@"^GPAL\d+_",
"");
5990 if (!grouped.TryGetValue(cleanedKey, out var list))
5992 list =
new List<object>();
5993 grouped[cleanedKey] = list;
5998 var result =
new Dictionary<string, object>(StringComparer.Ordinal);
5999 foreach (var kvp
in grouped)
6001 result[kvp.Key] = kvp.Value.Count == 1 ? kvp.Value[0] : (object)kvp.Value;
6004 if (result.Count == 1 && result.ContainsKey(
"value"))
6006 return result[
"value"];
6011 if (input is IEnumerable enumerable && !(input is
string))
6013 var list =
new List<object>();
6014 foreach (var item
in enumerable)
6016 list.Add(StripAndGroup(item));
6018 return list.Count == 0 ? null : list;
6024 private static object UnwrapSingleItemLists(
object input)
6026 if (input ==
null)
return null;
6030 if (input is IList list && list.Count == 1)
6032 object inner = UnwrapSingleItemLists(list[0]);
6033 if (inner ==
null || inner is
string || inner is ValueType)
6039 return new List<object> { inner };
6044 if (input is IList multiList && multiList.Count > 1)
6046 var unwrapped =
new List<object>();
6047 foreach (var item
in multiList)
6049 unwrapped.Add(UnwrapSingleItemLists(item));
6055 if (input is IDictionary dict)
6057 var result =
new Dictionary<string, object>(StringComparer.Ordinal);
6058 foreach (DictionaryEntry entry
in dict)
6060 string key = entry.Key?.ToString() ??
"";
6061 object value = UnwrapSingleItemLists(entry.Value);
6062 result[key] = value;
6065 if (result.Count == 1 && result.ContainsKey(
"value"))
6066 return result[
"value"];
6070 if (result.Count == 1 && result.ContainsKey(
"item"))
6072 var itemVal = result[
"item"];
6073 return itemVal is IList ? itemVal :
new List<object> { itemVal };
6081 internal static string GetXmlHeaderFromFile(
string inputFilename)
6083 if (!File.Exists(inputFilename))
6084 return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
6086 var lines = File.ReadAllLines(inputFilename);
6087 var headerLines =
new List<string>();
6089 foreach (var line
in lines)
6091 string trimmed = line.Trim();
6094 if (trimmed.StartsWith(
"<") && !trimmed.StartsWith(
"<?") && !trimmed.StartsWith(
"<!DOCTYPE") && !trimmed.StartsWith(
"<!--"))
6100 if (trimmed.StartsWith(
"<?xml") ||
6101 trimmed.StartsWith(
"<?xml-stylesheet") ||
6102 trimmed.StartsWith(
"<!DOCTYPE") ||
6103 trimmed.StartsWith(
"<!--"))
6105 headerLines.Add(line);
6110 if (headerLines.Count == 0)
6111 return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
6113 return string.Join(Environment.NewLine, headerLines) + Environment.NewLine;
6116 public static bool TryConvertToWaitTime(
object value, out WaitTime result)
6118 result = WaitTime.Immediate;
6123 GPALEventType.WARNING,
6124 "WaitTime treating NULL as ZERO",
6126 GPALObjectType.Converter);
6132 if (value is WaitTime wt)
6144 if (value is
string s)
6146 if (WaitTime.TryFromString(s, out result))
6150 if (WaitTime.TryFromDictionary((IDictionary<object, object>)value, out result))
6156 GPALEventType.WARNING,
6157 $
"WaitTime conversion failed: unsupported type [{value.GetType().FullName}]",
6159 GPALObjectType.Converter);
6163 internal static List<string> SplitRespectingQuotes(
string line,
char delimiter)
6165 var tokens =
new List<string>();
6166 var sb =
new StringBuilder();
6167 bool inQuotes =
false;
6169 for (
int i = 0; i < line.Length; i++)
6176 if (inQuotes && i + 1 < line.Length && line[i + 1] ==
'"')
6183 inQuotes = !inQuotes;
6186 else if (c == delimiter && !inQuotes)
6188 tokens.Add(sb.ToString().Trim());
6191 else if (c == delimiter && inQuotes)
6204 tokens.Add(sb.ToString().Trim());
6207 internal static string ExtractRawXmlFromBrowserSource(
string fullPageSource)
6209 if (
string.IsNullOrWhiteSpace(fullPageSource))
6210 return string.Empty;
6213 int startIndex = fullPageSource.IndexOf(
"<div id=\"webkit-xml-viewer-source-xml\">");
6214 if (startIndex == -1)
6217 if (fullPageSource.TrimStart().StartsWith(
"<?xml"))
6218 return fullPageSource;
6221 GPALEventType.DEBUG,
6222 "No webkit-xml-viewer wrapper found and content doesn't start with <?xml. Returning empty.",
6224 GPALObjectType.Other);
6225 return string.Empty;
6228 startIndex +=
"<div id=\"webkit-xml-viewer-source-xml\">".Length;
6230 int endIndex = fullPageSource.IndexOf(
"</div>", startIndex);
6231 if (endIndex == -1) endIndex = fullPageSource.Length;
6233 string innerHtml = fullPageSource.Substring(startIndex, endIndex - startIndex).Trim();
6240 innerHtml = innerHtml.Replace(
"<![CDATA[",
"").Replace(
"]]>",
"");
6244 #endregion <Helpers>