JsonDeserializer.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. using System;
  2. using System.CodeDom;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Collections.ObjectModel;
  6. using System.ComponentModel;
  7. using System.Globalization;
  8. using System.Linq;
  9. using System.Reflection;
  10. namespace FastReport.Utils.Json.Serialization
  11. {
  12. internal static class JsonDeserializer
  13. {
  14. private static readonly Dictionary<Type, ConstructorInfo> _ctorsCache = new Dictionary<Type, ConstructorInfo>();
  15. private static readonly Dictionary<Type, JsonPropertyInfo[]> _writablePropertiesCache =
  16. new Dictionary<Type, JsonPropertyInfo[]>();
  17. public static T Deserialize<T>(string json)
  18. {
  19. var instance = CreateInstance<T>();
  20. DeserializeProperties(instance, json);
  21. return instance;
  22. }
  23. public static object Deserialize(string json, Type type)
  24. {
  25. var instance = CreateInstance(type);
  26. DeserializeProperties(instance, json);
  27. return instance;
  28. }
  29. private static object Deserialize(JsonBase jsonBase, Type type)
  30. {
  31. var instance = CreateInstance(type);
  32. DeserializeProperties(instance, jsonBase, type);
  33. return instance;
  34. }
  35. private static void DeserializeProperties<T>(T instance, string json)
  36. {
  37. var jsonBase = JsonBase.FromString(json);
  38. DeserializeProperties(instance, jsonBase);
  39. }
  40. private static void DeserializeProperties<T>(T instance, JsonBase jsonBase)
  41. {
  42. var properties = GetWritableProperties<T>();
  43. DeserializeProperties(instance, jsonBase, properties);
  44. }
  45. private static void DeserializeProperties(object instance, JsonBase jsonBase, Type type)
  46. {
  47. var properties = GetWritableProperties(type);
  48. DeserializeProperties(instance, jsonBase, properties);
  49. }
  50. private static void DeserializeProperties(object instance, JsonBase jsonBase, JsonPropertyInfo[] properties)
  51. {
  52. foreach (var property in properties)
  53. {
  54. var propInfo = property.Info;
  55. var propName = property.Name;
  56. var obj = jsonBase[propName];
  57. obj = CastToType(obj, propInfo.PropertyType);
  58. #if COREWIN || CROSSPLATFORM || MONO // .Net 4.5 or greater
  59. propInfo.SetValue(instance, obj);
  60. #else
  61. propInfo.GetSetMethod(true)
  62. .Invoke(instance, BindingFlags.SetProperty, null, new[] { obj }, null);
  63. #endif
  64. }
  65. }
  66. private static object ConvertCollection(object obj, Type collectionType)
  67. {
  68. if (obj is JsonArray jsonArray)
  69. {
  70. bool isArray;
  71. Type elementType = GetElementType(collectionType, out isArray);
  72. if (isArray)
  73. {
  74. IList collection = Array.CreateInstance(elementType, jsonArray.Count);
  75. for (int i = 0; i < jsonArray.Count; i++)
  76. {
  77. var item = jsonArray[i];
  78. var converted = CastToType(item, elementType);
  79. collection[i] = converted;
  80. }
  81. return collection;
  82. }
  83. else
  84. {
  85. var genericTypes = collectionType.GetGenericArguments();
  86. var listType = typeof(List<>).MakeGenericType(genericTypes);
  87. var collection = (IList)Activator.CreateInstance(listType);
  88. foreach (var item in jsonArray)
  89. {
  90. var converted = CastToType(item, elementType);
  91. collection.Add(converted);
  92. }
  93. return collection;
  94. }
  95. }
  96. throw new Exception("Unknown array type " + obj.GetType().FullName);
  97. }
  98. private static object CastToType(object value, Type expectedType)
  99. {
  100. if (value == null)
  101. return null;
  102. object convertedValue;
  103. if (expectedType.IsPrimitive)
  104. {
  105. convertedValue = Convert.ChangeType(value, expectedType);
  106. }
  107. else if (expectedType == typeof(string))
  108. {
  109. convertedValue = value.ToString();
  110. }
  111. else if (expectedType == typeof(DateTime))
  112. {
  113. convertedValue = DateTime.Parse(value.ToString(), CultureInfo.InvariantCulture);
  114. }
  115. else if (expectedType.IsArray || typeof(IEnumerable).IsAssignableFrom(expectedType))
  116. {
  117. var actualType = value.GetType();
  118. var elemType = GetElementType(expectedType, out bool isArray);
  119. // BASE 64 check
  120. if (elemType == typeof(byte) && actualType == typeof(string))
  121. convertedValue = Convert.FromBase64String(value.ToString());
  122. else
  123. convertedValue = ConvertCollection(value, expectedType);
  124. }
  125. else if (expectedType.IsEnum)
  126. {
  127. convertedValue = Enum.Parse(expectedType, value.ToString(), true);
  128. }
  129. else // Custom type
  130. {
  131. if (value is JsonBase jsonBase)
  132. {
  133. convertedValue = Deserialize(jsonBase, expectedType);
  134. }
  135. else
  136. throw new Exception("Unknown type" + expectedType.FullName);
  137. }
  138. return convertedValue;
  139. }
  140. private static Type GetElementType(Type collection, out bool isArray)
  141. {
  142. isArray = collection.IsArray;
  143. if (isArray)
  144. {
  145. return collection.GetElementType();
  146. }
  147. else // IEnumerable, IEnumerable<>, ICollection ...
  148. {
  149. var genArgs = collection.GetGenericArguments();
  150. if (genArgs == null)
  151. return typeof(object);
  152. return genArgs[0];
  153. }
  154. }
  155. private static T CreateInstance<T>()
  156. {
  157. var type = typeof(T);
  158. var instance = (T)CreateInstance(type);
  159. return instance;
  160. }
  161. private static object CreateInstance(Type type)
  162. {
  163. var ctor = GetConstructor(type);
  164. var instance = ctor.Invoke(null);
  165. return instance;
  166. }
  167. private static ConstructorInfo GetConstructor(Type type)
  168. {
  169. if (_ctorsCache.ContainsKey(type))
  170. return _ctorsCache[type];
  171. var ctors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
  172. // Trying to find a parameterless ctor
  173. var availableCtor = ctors.Where(_ctor => _ctor.GetParameters().Length == 0).ToArray();
  174. if (availableCtor.Length == 0)
  175. throw new Exception("There are no available public parameterless constructors for this type");
  176. var ctor = availableCtor[0];
  177. #if COREWIN
  178. _ctorsCache.TryAdd(type, ctor);
  179. #else
  180. _ctorsCache.Add(type, ctor);
  181. #endif
  182. return ctor;
  183. }
  184. private static JsonPropertyInfo[] GetWritableProperties<T>()
  185. {
  186. var type = typeof(T);
  187. return GetWritableProperties(type);
  188. }
  189. private static JsonPropertyInfo[] GetWritableProperties(Type type)
  190. {
  191. if (_writablePropertiesCache.ContainsKey(type))
  192. return _writablePropertiesCache[type];
  193. var findProps = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
  194. var writableProps = findProps
  195. .Where(prop => Attribute.GetCustomAttribute(prop, typeof(JsonIgnoreAttribute)) == null)
  196. .Where(prop => prop.CanWrite).ToArray();
  197. var propInfoList = new List<JsonPropertyInfo>();
  198. foreach (var writableProp in writableProps)
  199. {
  200. var propInfo = JsonPropertyInfo.Parse(writableProp);
  201. propInfoList.Add(propInfo);
  202. }
  203. var propInfos = propInfoList.ToArray();
  204. #if COREWIN
  205. _writablePropertiesCache.TryAdd(type, propInfos);
  206. #else
  207. _writablePropertiesCache.Add(type, propInfos);
  208. #endif
  209. return propInfos;
  210. }
  211. }
  212. }