PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/Neo4jClient/Deserializer/CommonDeserializerMethods.cs

https://bitbucket.org/Readify/neo4jclient/
C# | 271 lines | 241 code | 24 blank | 6 comment | 66 complexity | 56b2862c62a92f04af15aa0600edb98b MD5 | raw file
Possible License(s): CC-BY-SA-3.0, BSD-3-Clause, GPL-2.0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Text.RegularExpressions;
  9. using Newtonsoft.Json;
  10. using Newtonsoft.Json.Linq;
  11. using RestSharp.Extensions;
  12. namespace Neo4jClient.Deserializer
  13. {
  14. class CommonDeserializerMethods
  15. {
  16. static readonly Regex DateRegex = new Regex(@"/Date\([-]?\d+([+-]\d+)?\)/");
  17. static readonly Regex DateTypeNameRegex = new Regex(@"(?<=(?<quote>['""])/)Date(?=\(.*?\)/\k<quote>)");
  18. public static string ReplaceAllDateInstacesWithNeoDates(string content)
  19. {
  20. // Replace all /Date(1234+0200)/ instances with /NeoDate(1234+0200)/
  21. return DateTypeNameRegex.Replace(content, "NeoDate");
  22. }
  23. public static DateTimeOffset? ParseDateTimeOffset(JToken value)
  24. {
  25. var rawValue = value.AsString();
  26. if (string.IsNullOrWhiteSpace(rawValue))
  27. return null;
  28. rawValue = rawValue.Replace("NeoDate", "Date");
  29. if (!DateRegex.IsMatch(rawValue))
  30. return null;
  31. var text = string.Format("{{\"a\":\"{0}\"}}", rawValue);
  32. var reader = new JsonTextReader(new StringReader(text));
  33. reader.Read(); // JsonToken.StartObject
  34. reader.Read(); // JsonToken.PropertyName
  35. return reader.ReadAsDateTimeOffset();
  36. }
  37. public static void SetPropertyValue(
  38. object targetObject,
  39. PropertyInfo propertyInfo,
  40. JToken value,
  41. CultureInfo culture,
  42. IEnumerable<TypeMapping> typeMappings)
  43. {
  44. if (value == null || value.Type == JTokenType.Null)
  45. return;
  46. var propertyType = propertyInfo.PropertyType;
  47. // check for nullable and extract underlying type
  48. if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
  49. {
  50. propertyType = propertyType.GetGenericArguments()[0];
  51. }
  52. var genericTypeDef = propertyType.IsGenericType ? propertyType.GetGenericTypeDefinition() : null;
  53. if (propertyType.IsPrimitive)
  54. {
  55. // no primitives can contain quotes so we can safely remove them
  56. // allows converting a json value like {"index": "1"} to an int
  57. var tmpVal = value.AsString().Replace("\"", string.Empty);
  58. propertyInfo.SetValue(targetObject, tmpVal.ChangeType(propertyType), null);
  59. }
  60. else if (propertyType.IsEnum)
  61. {
  62. var raw = value.AsString();
  63. var converted = Enum.Parse(propertyType, raw, false);
  64. propertyInfo.SetValue(targetObject, converted, null);
  65. }
  66. else if (propertyType == typeof(Uri))
  67. {
  68. var raw = value.AsString();
  69. var uri = new Uri(raw, UriKind.RelativeOrAbsolute);
  70. propertyInfo.SetValue(targetObject, uri, null);
  71. }
  72. else if (propertyType == typeof(string))
  73. {
  74. var raw = value.AsString();
  75. propertyInfo.SetValue(targetObject, raw, null);
  76. }
  77. else if (propertyType == typeof(DateTime))
  78. {
  79. throw new NotSupportedException("DateTime values are not supported. Use DateTimeOffset instead.");
  80. }
  81. else if (propertyType == typeof(DateTimeOffset))
  82. {
  83. var dateTimeOffset = ParseDateTimeOffset(value);
  84. if (dateTimeOffset.HasValue)
  85. propertyInfo.SetValue(targetObject, dateTimeOffset.Value, null);
  86. }
  87. else if (propertyType == typeof(Decimal))
  88. {
  89. var dec = Decimal.Parse(value.AsString(), culture);
  90. propertyInfo.SetValue(targetObject, dec, null);
  91. }
  92. else if (propertyType == typeof(Guid))
  93. {
  94. var raw = value.AsString();
  95. var guid = string.IsNullOrEmpty(raw) ? Guid.Empty : new Guid(raw);
  96. propertyInfo.SetValue(targetObject, guid, null);
  97. }
  98. else if (genericTypeDef == typeof(List<>))
  99. {
  100. var list = BuildList(propertyType, value.Children(), culture, typeMappings);
  101. propertyInfo.SetValue(targetObject, list, null);
  102. }
  103. else if (genericTypeDef == typeof(Dictionary<,>))
  104. {
  105. var keyType = propertyType.GetGenericArguments()[0];
  106. // only supports Dict<string, T>()
  107. if (keyType == typeof(string))
  108. {
  109. var dict = BuildDictionary(propertyType, value.Children(), culture, typeMappings);
  110. propertyInfo.SetValue(targetObject, dict, null);
  111. }
  112. }
  113. else
  114. {
  115. // nested objects
  116. object item;
  117. var mapping = typeMappings.SingleOrDefault(m => propertyType == m.PropertyTypeToTriggerMapping || genericTypeDef == m.PropertyTypeToTriggerMapping);
  118. if (mapping != null)
  119. {
  120. var newType = mapping.DetermineTypeToParseJsonIntoBasedOnPropertyType(propertyType);
  121. var rawItem = CreateAndMap(newType, value, culture, typeMappings);
  122. item = mapping.MutationCallback(rawItem);
  123. }
  124. else
  125. {
  126. item = CreateAndMap(propertyType, value, culture, typeMappings);
  127. }
  128. propertyInfo.SetValue(targetObject, item, null);
  129. }
  130. }
  131. static object CreateAndMap(Type type, JToken element, CultureInfo culture, IEnumerable<TypeMapping> typeMappings)
  132. {
  133. object instance;
  134. if (type.IsGenericType)
  135. {
  136. var genericTypeDef = type.GetGenericTypeDefinition();
  137. if (genericTypeDef == typeof(Dictionary<,>))
  138. {
  139. instance = BuildDictionary(type, element.Children(), culture, typeMappings);
  140. }
  141. else if (genericTypeDef == typeof(List<>))
  142. {
  143. instance = BuildList(type, element.Children(), culture, typeMappings);
  144. }
  145. else if (type == typeof(string))
  146. {
  147. instance = (string)element;
  148. }
  149. else
  150. {
  151. instance = Activator.CreateInstance(type);
  152. Map(instance, element, culture, typeMappings);
  153. }
  154. }
  155. else if (type == typeof(string))
  156. {
  157. instance = (string)element;
  158. }
  159. else
  160. {
  161. instance = Activator.CreateInstance(type);
  162. Map(instance, element, culture, typeMappings);
  163. }
  164. return instance;
  165. }
  166. public static void Map(object targetObject, JToken parentJsonToken, CultureInfo culture, IEnumerable<TypeMapping> typeMappings)
  167. {
  168. var objType = targetObject.GetType();
  169. var props = GetPropertiesForType(objType);
  170. foreach (var propertyName in props.Keys)
  171. {
  172. var propertyInfo = props[propertyName];
  173. var jsonToken = parentJsonToken[propertyName];
  174. SetPropertyValue(targetObject, propertyInfo, jsonToken, culture, typeMappings);
  175. }
  176. }
  177. public static IDictionary BuildDictionary(Type type, JEnumerable<JToken> elements, CultureInfo culture, IEnumerable<TypeMapping> typeMappings)
  178. {
  179. var dict = (IDictionary)Activator.CreateInstance(type);
  180. var valueType = type.GetGenericArguments()[1];
  181. foreach (JProperty child in elements)
  182. {
  183. var key = child.Name;
  184. var item = CreateAndMap(valueType, child.Value, culture, typeMappings);
  185. dict.Add(key, item);
  186. }
  187. return dict;
  188. }
  189. public static IList BuildList(Type type, JEnumerable<JToken> elements, CultureInfo culture, IEnumerable<TypeMapping> typeMappings)
  190. {
  191. var list = (IList)Activator.CreateInstance(type);
  192. var itemType = type
  193. .GetInterfaces()
  194. .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>))
  195. .Select(i => i.GetGenericArguments().First())
  196. .Single();
  197. foreach (var element in elements)
  198. {
  199. if (itemType.IsPrimitive)
  200. {
  201. var value = element as JValue;
  202. if (value != null)
  203. {
  204. list.Add(value.Value.ChangeType(itemType));
  205. }
  206. }
  207. else if (itemType == typeof(string))
  208. {
  209. list.Add(element.AsString());
  210. }
  211. else
  212. {
  213. var item = CreateAndMap(itemType, element, culture, typeMappings);
  214. list.Add(item);
  215. }
  216. }
  217. return list;
  218. }
  219. static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> PropertyInfoCache = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
  220. static readonly object PropertyInfoCacheLock = new object();
  221. static Dictionary<string, PropertyInfo> GetPropertiesForType(Type objType)
  222. {
  223. Dictionary<string, PropertyInfo> result;
  224. if (PropertyInfoCache.TryGetValue(objType, out result))
  225. return result;
  226. lock (PropertyInfoCacheLock)
  227. {
  228. if (PropertyInfoCache.TryGetValue(objType, out result))
  229. return result;
  230. var properties = objType
  231. .GetProperties()
  232. .Where(p => p.CanWrite)
  233. .Select(p =>
  234. {
  235. var attributes =
  236. (JsonPropertyAttribute[])p.GetCustomAttributes(typeof(JsonPropertyAttribute), true);
  237. return new
  238. {
  239. Name = attributes.Any() ? attributes.Single().PropertyName : p.Name,
  240. Property = p
  241. };
  242. });
  243. return properties.ToDictionary(p => p.Name, p => p.Property);
  244. }
  245. }
  246. }
  247. }